/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// System dependencies
#include <pthread.h>
#include <fcntl.h>

// Camera dependencies
#include "cam_semaphore.h"
#include "mm_camera_dbg.h"
#include "mm_camera_interface.h"
#include "mm_camera.h"

extern mm_camera_obj_t* mm_camera_util_get_camera_by_handler(uint32_t cam_handler);
extern mm_channel_t * mm_camera_util_get_channel_by_handler(mm_camera_obj_t * cam_obj,
                                                            uint32_t handler);
/* Static frame sync info used between different camera channels*/
static mm_channel_frame_sync_info_t fs = { .num_cam =0, .pos = 0};
/* Frame sync info access lock */
static pthread_mutex_t fs_lock = PTHREAD_MUTEX_INITIALIZER;

/* internal function declare goes here */
int32_t mm_channel_qbuf(mm_channel_t *my_obj,
                        mm_camera_buf_def_t *buf);
int32_t mm_channel_cancel_buf(mm_channel_t *my_obj,
                        uint32_t stream_id, uint32_t buf_idx);
int32_t mm_channel_init(mm_channel_t *my_obj,
                        mm_camera_channel_attr_t *attr,
                        mm_camera_buf_notify_t channel_cb,
                        void *userdata);
void mm_channel_release(mm_channel_t *my_obj);
uint32_t mm_channel_add_stream(mm_channel_t *my_obj);
int32_t mm_channel_del_stream(mm_channel_t *my_obj,
                                   uint32_t stream_id);
uint32_t mm_channel_link_stream(mm_channel_t *my_obj,
        mm_camera_stream_link_t *stream_link);
int32_t mm_channel_config_stream(mm_channel_t *my_obj,
                                 uint32_t stream_id,
                                 mm_camera_stream_config_t *config);
int32_t mm_channel_get_bundle_info(mm_channel_t *my_obj,
                                   cam_bundle_config_t *bundle_info);
int32_t mm_channel_start(mm_channel_t *my_obj);
int32_t mm_channel_stop(mm_channel_t *my_obj);
int32_t mm_channel_request_super_buf(mm_channel_t *my_obj,
        mm_camera_req_buf_t *buf);
int32_t mm_channel_cancel_super_buf_request(mm_channel_t *my_obj);
int32_t mm_channel_flush_super_buf_queue(mm_channel_t *my_obj,
                                         uint32_t frame_idx,
                                         cam_stream_type_t stream_type);
int32_t mm_channel_config_notify_mode(mm_channel_t *my_obj,
                                      mm_camera_super_buf_notify_mode_t notify_mode);
int32_t mm_channel_start_zsl_snapshot(mm_channel_t *my_obj);
int32_t mm_channel_stop_zsl_snapshot(mm_channel_t *my_obj);
int32_t mm_channel_superbuf_flush(mm_channel_t* my_obj,
        mm_channel_queue_t * queue, cam_stream_type_t cam_type);
int32_t mm_channel_set_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload);
int32_t mm_channel_get_queued_buf_count(mm_channel_t *my_obj,
        uint32_t stream_id);

int32_t mm_channel_get_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload);
int32_t mm_channel_do_stream_action(mm_channel_t *my_obj,
                                    mm_evt_paylod_do_stream_action_t *payload);
int32_t mm_channel_map_stream_buf(mm_channel_t *my_obj,
                                  cam_buf_map_type *payload);
int32_t mm_channel_map_stream_bufs(mm_channel_t *my_obj,
                                   cam_buf_map_type_list *payload);
int32_t mm_channel_unmap_stream_buf(mm_channel_t *my_obj,
                                    cam_buf_unmap_type *payload);

/* state machine function declare */
int32_t mm_channel_fsm_fn_notused(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_channel_fsm_fn_stopped(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_channel_fsm_fn_active(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);
int32_t mm_channel_fsm_fn_paused(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val);

/* channel super queue functions */
int32_t mm_channel_superbuf_queue_init(mm_channel_queue_t * queue);
int32_t mm_channel_superbuf_queue_deinit(mm_channel_queue_t * queue);
int32_t mm_channel_superbuf_comp_and_enqueue(mm_channel_t *ch_obj,
                                             mm_channel_queue_t * queue,
                                             mm_camera_buf_info_t *buf);
mm_channel_queue_node_t* mm_channel_superbuf_dequeue(
        mm_channel_queue_t * queue, mm_channel_t *ch_obj);
int32_t mm_channel_superbuf_bufdone_overflow(mm_channel_t *my_obj,
                                             mm_channel_queue_t *queue);
int32_t mm_channel_superbuf_skip(mm_channel_t *my_obj,
                                 mm_channel_queue_t *queue);

static int32_t mm_channel_proc_general_cmd(mm_channel_t *my_obj,
                                           mm_camera_generic_cmd_t *p_gen_cmd);
int32_t mm_channel_superbuf_flush_matched(mm_channel_t* my_obj,
                                          mm_channel_queue_t * queue);

/* Start of Frame Sync util methods */
void mm_frame_sync_reset();
int32_t mm_frame_sync_register_channel(mm_channel_t *ch_obj);
int32_t mm_frame_sync_unregister_channel(mm_channel_t *ch_obj);
int32_t mm_frame_sync_add(uint32_t frame_id, mm_channel_t *ch_obj);
int32_t mm_frame_sync_remove(uint32_t frame_id);
uint32_t mm_frame_sync_find_matched(uint8_t oldest);
int8_t mm_frame_sync_find_frame_index(uint32_t frame_id);
void mm_frame_sync_lock_queues();
void mm_frame_sync_unlock_queues();
void mm_channel_node_qbuf(mm_channel_t *ch_obj, mm_channel_queue_node_t *node);
/* End of Frame Sync Util methods */
void mm_channel_send_super_buf(mm_channel_node_info_t *info);
mm_channel_queue_node_t* mm_channel_superbuf_dequeue_frame_internal(
        mm_channel_queue_t * queue, uint32_t frame_idx);

/*===========================================================================
 * FUNCTION   : mm_channel_util_get_stream_by_handler
 *
 * DESCRIPTION: utility function to get a stream object from its handle
 *
 * PARAMETERS :
 *   @cam_obj: ptr to a channel object
 *   @handler: stream handle
 *
 * RETURN     : ptr to a stream object.
 *              NULL if failed.
 *==========================================================================*/
mm_stream_t * mm_channel_util_get_stream_by_handler(
                                    mm_channel_t * ch_obj,
                                    uint32_t handler)
{
    int i;
    mm_stream_t *s_obj = NULL;
    for(i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if ((MM_STREAM_STATE_NOTUSED != ch_obj->streams[i].state) &&
            (handler == ch_obj->streams[i].my_hdl)) {
            s_obj = &ch_obj->streams[i];
            break;
        }
    }
    return s_obj;
}

/*===========================================================================
 * FUNCTION   : mm_channel_dispatch_super_buf
 *
 * DESCRIPTION: dispatch super buffer of bundle to registered user
 *
 * PARAMETERS :
 *   @cmd_cb  : ptr storing matched super buf information
 *   @userdata: user data ptr
 *
 * RETURN     : none
 *==========================================================================*/
static void mm_channel_dispatch_super_buf(mm_camera_cmdcb_t *cmd_cb,
                                          void* user_data)
{
    mm_channel_t * my_obj = (mm_channel_t *)user_data;

    if (NULL == my_obj) {
        return;
    }

    if (MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB != cmd_cb->cmd_type) {
        LOGE("Wrong cmd_type (%d) for super buf dataCB",
                    cmd_cb->cmd_type);
        return;
    }

    if (my_obj->bundle.super_buf_notify_cb) {
        my_obj->bundle.super_buf_notify_cb(&cmd_cb->u.superbuf, my_obj->bundle.user_data);
    }
}

/*===========================================================================
 * FUNCTION   : mm_channel_process_stream_buf
 *
 * DESCRIPTION: handle incoming buffer from stream in a bundle. In this function,
 *              matching logic will be performed on incoming stream frames.
 *              Will depends on the bundle attribute, either storing matched frames
 *              in the superbuf queue, or sending matched superbuf frames to upper
 *              layer through registered callback.
 *
 * PARAMETERS :
 *   @cmd_cb  : ptr storing matched super buf information
 *   @userdata: user data ptr
 *
 * RETURN     : none
 *==========================================================================*/
static void mm_channel_process_stream_buf(mm_camera_cmdcb_t * cmd_cb,
                                          void *user_data)
{
    mm_camera_super_buf_notify_mode_t notify_mode;
    mm_channel_queue_node_t *node = NULL;
    mm_channel_t *ch_obj = (mm_channel_t *)user_data;
    uint32_t i = 0;
    /* Set expected frame id to a future frame idx, large enough to wait
    * for good_frame_idx_range, and small enough to still capture an image */
    uint8_t needStartZSL = FALSE;

    if (NULL == ch_obj) {
        return;
    }
    if (MM_CAMERA_CMD_TYPE_DATA_CB  == cmd_cb->cmd_type) {
        /* comp_and_enqueue */
        mm_channel_superbuf_comp_and_enqueue(
                        ch_obj,
                        &ch_obj->bundle.superbuf_queue,
                        &cmd_cb->u.buf);
    } else if (MM_CAMERA_CMD_TYPE_REQ_DATA_CB  == cmd_cb->cmd_type) {
        /* skip frames if needed */
        ch_obj->pending_cnt = cmd_cb->u.req_buf.num_buf_requested;
        ch_obj->pending_retro_cnt = cmd_cb->u.req_buf.num_retro_buf_requested;
        ch_obj->req_type = cmd_cb->u.req_buf.type;
        ch_obj->bWaitForPrepSnapshotDone = 0;

        LOGH("pending cnt (%d), retro count (%d)"
                "req_type (%d) is_primary (%d)",
                 ch_obj->pending_cnt, ch_obj->pending_retro_cnt,
                ch_obj->req_type, cmd_cb->u.req_buf.primary_only);
        if (!ch_obj->pending_cnt || (ch_obj->pending_retro_cnt > ch_obj->pending_cnt)) {
          ch_obj->pending_retro_cnt = ch_obj->pending_cnt;
        }
        if (ch_obj->pending_retro_cnt > 0) {
          LOGL("Resetting need Led Flash!!!");
          ch_obj->needLEDFlash = 0;
        }
        ch_obj->stopZslSnapshot = 0;
        ch_obj->unLockAEC = 0;

        mm_channel_superbuf_skip(ch_obj, &ch_obj->bundle.superbuf_queue);

    } else if (MM_CAMERA_CMD_TYPE_START_ZSL == cmd_cb->cmd_type) {
            ch_obj->manualZSLSnapshot = TRUE;
            mm_camera_start_zsl_snapshot(ch_obj->cam_obj);
    } else if (MM_CAMERA_CMD_TYPE_STOP_ZSL == cmd_cb->cmd_type) {
            ch_obj->manualZSLSnapshot = FALSE;
            mm_camera_stop_zsl_snapshot(ch_obj->cam_obj);
    } else if (MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY == cmd_cb->cmd_type) {
           ch_obj->bundle.superbuf_queue.attr.notify_mode = cmd_cb->u.notify_mode;
    } else if (MM_CAMERA_CMD_TYPE_FLUSH_QUEUE  == cmd_cb->cmd_type) {
        ch_obj->bundle.superbuf_queue.expected_frame_id = cmd_cb->u.flush_cmd.frame_idx;
        mm_channel_superbuf_flush(ch_obj,
                &ch_obj->bundle.superbuf_queue, cmd_cb->u.flush_cmd.stream_type);
        cam_sem_post(&(ch_obj->cmd_thread.sync_sem));
        return;
    } else if (MM_CAMERA_CMD_TYPE_GENERAL == cmd_cb->cmd_type) {
        LOGH("MM_CAMERA_CMD_TYPE_GENERAL");
        switch (cmd_cb->u.gen_cmd.type) {
            case MM_CAMERA_GENERIC_CMD_TYPE_AE_BRACKETING:
            case MM_CAMERA_GENERIC_CMD_TYPE_AF_BRACKETING: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMDTYPE_AF_BRACKETING %u",
                      start);
                mm_channel_superbuf_flush(ch_obj,
                        &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);

                if (start) {
                    LOGH("need AE bracketing, start zsl snapshot");
                    ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_WAIT_GOOD_FRAME_IDX;
                } else {
                    ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_OFF;
                }
            }
                break;
            case MM_CAMERA_GENERIC_CMD_TYPE_FLASH_BRACKETING: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMDTYPE_FLASH_BRACKETING %u",
                      start);
                mm_channel_superbuf_flush(ch_obj,
                        &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);

                if (start) {
                    LOGH("need flash bracketing");
                    ch_obj->isFlashBracketingEnabled = TRUE;
                } else {
                    ch_obj->isFlashBracketingEnabled = FALSE;
                }
            }
                break;
            case MM_CAMERA_GENERIC_CMD_TYPE_ZOOM_1X: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMD_TYPE_ZOOM_1X %u",
                      start);
                mm_channel_superbuf_flush(ch_obj,
                        &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);

                if (start) {
                    LOGH("need zoom 1x frame");
                    ch_obj->isZoom1xFrameRequested = TRUE;
                } else {
                    ch_obj->isZoom1xFrameRequested = FALSE;
                }
            }
                break;
            case MM_CAMERA_GENERIC_CMD_TYPE_CAPTURE_SETTING: {
                uint32_t start = cmd_cb->u.gen_cmd.payload[0];
                LOGI("MM_CAMERA_GENERIC_CMD_TYPE_CAPTURE_SETTING %u num_batch = %d",
                      start, cmd_cb->u.gen_cmd.frame_config.num_batch);

                if (start) {
                    memset(&ch_obj->frameConfig, 0, sizeof(cam_capture_frame_config_t));
                    for (i = 0; i < cmd_cb->u.gen_cmd.frame_config.num_batch; i++) {
                        if (cmd_cb->u.gen_cmd.frame_config.configs[i].type
                                != CAM_CAPTURE_RESET) {
                            ch_obj->frameConfig.configs[
                                    ch_obj->frameConfig.num_batch] =
                                    cmd_cb->u.gen_cmd.frame_config.configs[i];
                            ch_obj->frameConfig.num_batch++;
                            LOGH("capture setting frame = %d type = %d",
                                    i,ch_obj->frameConfig.configs[
                                    ch_obj->frameConfig.num_batch].type);
                        }
                    }
                    LOGD("Capture setting Batch Count %d",
                              ch_obj->frameConfig.num_batch);
                    ch_obj->isConfigCapture = TRUE;
                } else {
                    ch_obj->isConfigCapture = FALSE;
                    memset(&ch_obj->frameConfig, 0, sizeof(cam_capture_frame_config_t));
                }
                ch_obj->cur_capture_idx = 0;
                memset(ch_obj->capture_frame_id, 0, sizeof(uint8_t) * MAX_CAPTURE_BATCH_NUM);
                break;
            }
            default:
                LOGE("Error: Invalid command");
                break;
        }
    }
    notify_mode = ch_obj->bundle.superbuf_queue.attr.notify_mode;

    /*Handle use case which does not need start ZSL even in unified case*/
    if ((ch_obj->pending_cnt > 0)
            && (ch_obj->isConfigCapture)
            && (ch_obj->manualZSLSnapshot == FALSE)
            && (ch_obj->startZSlSnapshotCalled == FALSE)) {
        needStartZSL = TRUE;
        for (i = ch_obj->cur_capture_idx;
                i < ch_obj->frameConfig.num_batch;
                i++) {
            cam_capture_type type = ch_obj->frameConfig.configs[i].type;
            if (((type == CAM_CAPTURE_FLASH) && (!ch_obj->needLEDFlash))
                    || ((type == CAM_CAPTURE_LOW_LIGHT) && (!ch_obj->needLowLightZSL))) {
                /*For flash and low light capture, start ZSL is triggered only if needed*/
                needStartZSL = FALSE;
                break;
            }
        }
    }

    if ((ch_obj->isConfigCapture)
            && (needStartZSL)) {
        for (i = ch_obj->cur_capture_idx;
                i < ch_obj->frameConfig.num_batch;
                i++) {
            ch_obj->capture_frame_id[i] =
                    ch_obj->bundle.superbuf_queue.expected_frame_id
                    + MM_CAMERA_MAX_FUTURE_FRAME_WAIT;
        }

        /* Need to Flush the queue and trigger frame config */
        mm_channel_superbuf_flush(ch_obj,
                &ch_obj->bundle.superbuf_queue, CAM_STREAM_TYPE_DEFAULT);
        LOGI("TRIGGER Start ZSL");
        mm_camera_start_zsl_snapshot(ch_obj->cam_obj);
        ch_obj->startZSlSnapshotCalled = TRUE;
        ch_obj->burstSnapNum = ch_obj->pending_cnt;
        ch_obj->bWaitForPrepSnapshotDone = 0;
    } else if ((ch_obj->pending_cnt > 0)
        && ((ch_obj->needLEDFlash == TRUE) ||
        (MM_CHANNEL_BRACKETING_STATE_OFF != ch_obj->bracketingState))
        && (ch_obj->manualZSLSnapshot == FALSE)
        && ch_obj->startZSlSnapshotCalled == FALSE) {

        LOGI("TRIGGER Start ZSL for Flash");
        mm_camera_start_zsl_snapshot(ch_obj->cam_obj);
        ch_obj->startZSlSnapshotCalled = TRUE;
        ch_obj->burstSnapNum = ch_obj->pending_cnt;
        ch_obj->bWaitForPrepSnapshotDone = 0;
    } else if (((ch_obj->pending_cnt == 0) || (ch_obj->stopZslSnapshot == 1))
            && (ch_obj->manualZSLSnapshot == FALSE)
            && (ch_obj->startZSlSnapshotCalled == TRUE)) {
        LOGI("TRIGGER Stop ZSL for cancel picture");
        mm_camera_stop_zsl_snapshot(ch_obj->cam_obj);
        // Unlock AEC
        ch_obj->startZSlSnapshotCalled = FALSE;
        ch_obj->needLEDFlash = FALSE;
        ch_obj->burstSnapNum = 0;
        ch_obj->stopZslSnapshot = 0;
        ch_obj->bWaitForPrepSnapshotDone = 0;
        ch_obj->unLockAEC = 1;
        ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_OFF;
        ch_obj->isConfigCapture = FALSE;
    }
    /* bufdone for overflowed bufs */
    mm_channel_superbuf_bufdone_overflow(ch_obj, &ch_obj->bundle.superbuf_queue);

    LOGD("Super Buffer received, pending_cnt=%d queue cnt = %d expected = %d",
            ch_obj->pending_cnt, ch_obj->bundle.superbuf_queue.match_cnt,
            ch_obj->bundle.superbuf_queue.expected_frame_id);

    /* dispatch frame if pending_cnt>0 or is in continuous streaming mode */
    while (((ch_obj->pending_cnt > 0) ||
             (MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS == notify_mode)) &&
             (!ch_obj->bWaitForPrepSnapshotDone)) {

        /* dequeue */
        mm_channel_node_info_t info;
        memset(&info, 0x0, sizeof(info));

        if (ch_obj->req_type == MM_CAMERA_REQ_FRAME_SYNC_BUF) {
            // Lock the Queues
            mm_frame_sync_lock_queues();
            uint32_t match_frame = mm_frame_sync_find_matched(FALSE);
            if (match_frame) {
                uint8_t j = 0;
                for (j = 0; j < MAX_NUM_CAMERA_PER_BUNDLE; j++) {
                    if (fs.ch_obj[j]) {
                        mm_channel_queue_t *ch_queue =
                                &fs.ch_obj[j]->bundle.superbuf_queue;
                        if (ch_queue == NULL) {
                            LOGW("Channel queue is NULL");
                            break;
                        }
                        node = mm_channel_superbuf_dequeue_frame_internal(
                                ch_queue, match_frame);
                        if (node != NULL) {
                            info.ch_obj[info.num_nodes] = fs.ch_obj[j];
                            info.node[info.num_nodes] = node;
                            info.num_nodes++;
                            LOGH("Added ch(%p) to node ,num nodes %d",
                                     fs.ch_obj[j], info.num_nodes);
                        }
                    }
                }
                mm_frame_sync_remove(match_frame);
                LOGI("match frame %d", match_frame);
                if (info.num_nodes != fs.num_cam) {
                    LOGI("num node %d != num cam (%d) Debug this",
                             info.num_nodes, fs.num_cam);
                    uint8_t j = 0;
                    // free super buffers from various nodes
                    for (j = 0; j < info.num_nodes; j++) {
                        if (info.node[j]) {
                            mm_channel_node_qbuf(info.ch_obj[j], info.node[j]);
                            free(info.node[j]);
                        }
                    }
                    // we should not use it as matched dual camera frames
                    info.num_nodes = 0;
                }
            }
            mm_frame_sync_unlock_queues();
        } else {
           node = mm_channel_superbuf_dequeue(&ch_obj->bundle.superbuf_queue, ch_obj);
           if (node != NULL) {
               if (ch_obj->isConfigCapture &&
                       ((node->frame_idx <
                        ch_obj->capture_frame_id[ch_obj->cur_capture_idx]))) {
                   uint8_t i;
                   LOGD("Not expected super buffer. frameID = %d expected = %d",
                           node->frame_idx, ch_obj->capture_frame_id[ch_obj->cur_capture_idx]);
                   for (i = 0; i < node->num_of_bufs; i++) {
                       mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
                   }
                   free(node);
               } else {
                   info.num_nodes = 1;
                   info.ch_obj[0] = ch_obj;
                   info.node[0] = node;
               }
            }
        }
        if (info.num_nodes > 0) {
            /* decrease pending_cnt */
            if (MM_CAMERA_SUPER_BUF_NOTIFY_BURST == notify_mode) {
                ch_obj->pending_cnt--;
                if (ch_obj->pending_retro_cnt > 0) {
                  if (ch_obj->pending_retro_cnt == 1) {
                    ch_obj->bWaitForPrepSnapshotDone = 1;
                  }
                  ch_obj->pending_retro_cnt--;
                }

                if (((ch_obj->pending_cnt == 0) ||
                      (ch_obj->stopZslSnapshot == 1)) &&
                      (ch_obj->manualZSLSnapshot == FALSE) &&
                       ch_obj->startZSlSnapshotCalled == TRUE) {
                    LOGI("TRIGGER Stop ZSL. All frame received");
                    mm_camera_stop_zsl_snapshot(ch_obj->cam_obj);
                    ch_obj->startZSlSnapshotCalled = FALSE;
                    ch_obj->burstSnapNum = 0;
                    ch_obj->stopZslSnapshot = 0;
                    ch_obj->unLockAEC = 1;
                    ch_obj->needLEDFlash = FALSE;
                    ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_OFF;
                    ch_obj->isConfigCapture = FALSE;
                }

                if (ch_obj->isConfigCapture && ch_obj->cur_capture_idx < MAX_CAPTURE_BATCH_NUM) {
                    if (ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames != 0) {
                        ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames--;
                    } else {
                        LOGW("Invalid frame config batch index %d max batch = %d",
                                ch_obj->cur_capture_idx, ch_obj->frameConfig.num_batch);
                    }

                    if (ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames == 0) {
                        //Received all frames for current batch
                        ch_obj->cur_capture_idx++;
                        ch_obj->bundle.superbuf_queue.expected_frame_id =
                                ch_obj->capture_frame_id[ch_obj->cur_capture_idx];
                        ch_obj->bundle.superbuf_queue.good_frame_id =
                                ch_obj->capture_frame_id[ch_obj->cur_capture_idx];
                    } else {
                        LOGH("Need %d frames more for batch %d",
                                ch_obj->frameConfig.configs[ch_obj->cur_capture_idx].num_frames,
                                ch_obj->cur_capture_idx);
                    }
                }
            }
            /* dispatch superbuf */
            mm_channel_send_super_buf(&info);
        } else {
            /* no superbuf avail, break the loop */
            break;
        }
    }
}

/*===========================================================================
 * FUNCTION   : mm_channel_send_super_buf
 *
 * DESCRIPTION: Send super buffers to HAL
 *
 * PARAMETERS :
 *   @info  : Info of super buffers to be sent in callback
 *
 * RETURN     : None
 *==========================================================================*/
void mm_channel_send_super_buf(mm_channel_node_info_t *info)
{
    if (!info || !info->num_nodes){
        LOGE("X Error!! Info invalid");
        return;
    }
    mm_channel_queue_node_t *node = NULL;

    LOGH("num nodes %d to send", info->num_nodes);
    uint32_t idx = 0;
    mm_channel_t *ch_obj = NULL;
    for (idx = 0; idx < info->num_nodes; idx++) {
        node = info->node[idx];
        ch_obj = info->ch_obj[idx];
        if ((ch_obj) && (NULL != ch_obj->bundle.super_buf_notify_cb) && node) {
            mm_camera_cmdcb_t* cb_node = NULL;
            LOGD("Send superbuf to HAL, pending_cnt=%d",
                     ch_obj->pending_cnt);
            /* send cam_sem_post to wake up cb thread to dispatch super buffer */
            cb_node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
            if (NULL != cb_node) {
                memset(cb_node, 0, sizeof(mm_camera_cmdcb_t));
                cb_node->cmd_type = MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB;
                cb_node->u.superbuf.num_bufs = node->num_of_bufs;
                uint8_t i = 0;
                for (i = 0; i < node->num_of_bufs; i++) {
                    cb_node->u.superbuf.bufs[i] = node->super_buf[i].buf;
                }
                cb_node->u.superbuf.camera_handle = ch_obj->cam_obj->my_hdl;
                cb_node->u.superbuf.ch_id = ch_obj->my_hdl;
                cb_node->u.superbuf.bReadyForPrepareSnapshot =
                        ch_obj->bWaitForPrepSnapshotDone;
                if (ch_obj->unLockAEC == 1) {
                    cb_node->u.superbuf.bUnlockAEC = 1;
                    LOGH("Unlocking AEC");
                    ch_obj->unLockAEC = 0;
                }
                /* enqueue to cb thread */
                cam_queue_enq(&(ch_obj->cb_thread.cmd_queue), cb_node);
                /* wake up cb thread */
                cam_sem_post(&(ch_obj->cb_thread.cmd_sem));
                LOGH("Sent super buf for node[%d] ", idx);

            } else {
                LOGE("No memory for mm_camera_node_t");
                /* buf done with the unused super buf */
                uint8_t i = 0;
                for (i = 0; i < node->num_of_bufs; i++) {
                    mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
                }
            }
            free(node);
        } else if ((ch_obj != NULL) && (node != NULL)) {
            /* buf done with the unused super buf */
            uint8_t i;
            for (i = 0; i < node->num_of_bufs; i++) {
                mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
            }
            free(node);
        } else {
            LOGE("node is NULL, debug this");
        }
    }
}

/*===========================================================================
 * FUNCTION   : mm_channel_reg_stream_buf_cb
 *
 * DESCRIPTION: Register callback for stream buffer
 *
 * PARAMETERS :
 *   @my_obj     : Channel object
 *   @stream_id  : stream that will be linked
 *   @buf_cb     : special callback needs to be registered for stream buffer
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 --  failure
 *==========================================================================*/
int32_t mm_channel_reg_stream_buf_cb (mm_channel_t* my_obj,
        uint32_t stream_id, mm_stream_data_cb_t buf_cb)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
            stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }
        rc = mm_stream_reg_buf_cb(s_obj, buf_cb);
    }

    return rc;

}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn
 *
 * DESCRIPTION: channel finite state machine entry function. Depends on channel
 *              state, incoming event will be handled differently.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = -1;

    LOGD("E state = %d", my_obj->state);
    switch (my_obj->state) {
    case MM_CHANNEL_STATE_NOTUSED:
        rc = mm_channel_fsm_fn_notused(my_obj, evt, in_val, out_val);
        break;
    case MM_CHANNEL_STATE_STOPPED:
        rc = mm_channel_fsm_fn_stopped(my_obj, evt, in_val, out_val);
        break;
    case MM_CHANNEL_STATE_ACTIVE:
        rc = mm_channel_fsm_fn_active(my_obj, evt, in_val, out_val);
        break;
    case MM_CHANNEL_STATE_PAUSED:
        rc = mm_channel_fsm_fn_paused(my_obj, evt, in_val, out_val);
        break;
    default:
        LOGD("Not a valid state (%d)", my_obj->state);
        break;
    }

    /* unlock ch_lock */
    pthread_mutex_unlock(&my_obj->ch_lock);
    LOGD("X rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_notused
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in NOT_USED state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_notused(mm_channel_t *my_obj,
                                  mm_channel_evt_type_t evt,
                                  void * in_val,
                                  void * out_val)
{
    int32_t rc = -1;

    switch (evt) {
    default:
        LOGE("invalid state (%d) for evt (%d), in(%p), out(%p)",
                    my_obj->state, evt, in_val, out_val);
        break;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_stopped
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in STOPPED state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_stopped(mm_channel_t *my_obj,
                                  mm_channel_evt_type_t evt,
                                  void * in_val,
                                  void * out_val)
{
    int32_t rc = 0;
    LOGD("E evt = %d", evt);
    switch (evt) {
    case MM_CHANNEL_EVT_ADD_STREAM:
        {
            uint32_t s_hdl = 0;
            s_hdl = mm_channel_add_stream(my_obj);
            *((uint32_t*)out_val) = s_hdl;
            rc = 0;
        }
        break;
    case MM_CHANNEL_EVT_LINK_STREAM:
        {
            mm_camera_stream_link_t *stream_link = NULL;
            uint32_t s_hdl = 0;
            stream_link = (mm_camera_stream_link_t *) in_val;
            s_hdl = mm_channel_link_stream(my_obj, stream_link);
            *((uint32_t*)out_val) = s_hdl;
            rc = 0;
        }
        break;
    case MM_CHANNEL_EVT_DEL_STREAM:
        {
            uint32_t s_id = *((uint32_t *)in_val);
            rc = mm_channel_del_stream(my_obj, s_id);
        }
        break;
    case MM_CHANNEL_EVT_START:
        {
            rc = mm_channel_start(my_obj);
            /* first stream started in stopped state
             * move to active state */
            if (0 == rc) {
                my_obj->state = MM_CHANNEL_STATE_ACTIVE;
            }
        }
        break;
    case MM_CHANNEL_EVT_CONFIG_STREAM:
        {
            mm_evt_paylod_config_stream_t *payload =
                (mm_evt_paylod_config_stream_t *)in_val;
            rc = mm_channel_config_stream(my_obj,
                                          payload->stream_id,
                                          payload->config);
        }
        break;
    case MM_CHANNEL_EVT_GET_BUNDLE_INFO:
        {
            cam_bundle_config_t *payload =
                (cam_bundle_config_t *)in_val;
            rc = mm_channel_get_bundle_info(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_DELETE:
        {
            mm_channel_release(my_obj);
            rc = 0;
        }
        break;
    case MM_CHANNEL_EVT_SET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_set_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_QUEUED_BUF_COUNT:
        {
            uint32_t stream_id = *((uint32_t *)in_val);
            rc = mm_channel_get_queued_buf_count(my_obj, stream_id);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_get_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_DO_STREAM_ACTION:
        {
            mm_evt_paylod_do_stream_action_t *payload =
                (mm_evt_paylod_do_stream_action_t *)in_val;
            rc = mm_channel_do_stream_action(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUF:
        {
            cam_buf_map_type *payload =
                (cam_buf_map_type *)in_val;
            rc = mm_channel_map_stream_buf(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUFS:
        {
            cam_buf_map_type_list *payload =
                (cam_buf_map_type_list *)in_val;
            rc = mm_channel_map_stream_bufs(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_UNMAP_STREAM_BUF:
        {
            cam_buf_unmap_type *payload =
                (cam_buf_unmap_type *)in_val;
            rc = mm_channel_unmap_stream_buf(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_REG_STREAM_BUF_CB:
        {
            mm_evt_paylod_reg_stream_buf_cb *payload =
                (mm_evt_paylod_reg_stream_buf_cb *)in_val;
            rc = mm_channel_reg_stream_buf_cb (my_obj,
                    payload->stream_id, payload->buf_cb);
        }
        break;
    default:
        LOGE("invalid state (%d) for evt (%d)",
                    my_obj->state, evt);
        break;
    }
    LOGD("E rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_active
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in ACTIVE state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_active(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = 0;

    LOGD("E evt = %d", evt);
    switch (evt) {
    case MM_CHANNEL_EVT_STOP:
        {
            rc = mm_channel_stop(my_obj);
            my_obj->state = MM_CHANNEL_STATE_STOPPED;
        }
        break;
    case MM_CHANNEL_EVT_REQUEST_SUPER_BUF:
        {
            mm_camera_req_buf_t *payload =
                    (mm_camera_req_buf_t *)in_val;
            rc = mm_channel_request_super_buf(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_CANCEL_REQUEST_SUPER_BUF:
        {
            rc = mm_channel_cancel_super_buf_request(my_obj);
        }
        break;
    case MM_CHANNEL_EVT_FLUSH_SUPER_BUF_QUEUE:
        {
            uint32_t frame_idx = *((uint32_t *)in_val);
            rc = mm_channel_flush_super_buf_queue(my_obj, frame_idx, CAM_STREAM_TYPE_DEFAULT);
        }
        break;
    case MM_CHANNEL_EVT_START_ZSL_SNAPSHOT:
        {
            rc = mm_channel_start_zsl_snapshot(my_obj);
        }
        break;
    case MM_CHANNEL_EVT_STOP_ZSL_SNAPSHOT:
        {
            rc = mm_channel_stop_zsl_snapshot(my_obj);
        }
        break;
    case MM_CHANNEL_EVT_CONFIG_NOTIFY_MODE:
        {
            mm_camera_super_buf_notify_mode_t notify_mode =
                *((mm_camera_super_buf_notify_mode_t *)in_val);
            rc = mm_channel_config_notify_mode(my_obj, notify_mode);
        }
        break;
    case MM_CHANNEL_EVT_SET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_set_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_QUEUED_BUF_COUNT:
        {
            uint32_t stream_id = *((uint32_t *)in_val);
            rc = mm_channel_get_queued_buf_count(my_obj, stream_id);
        }
        break;
    case MM_CHANNEL_EVT_GET_STREAM_PARM:
        {
            mm_evt_paylod_set_get_stream_parms_t *payload =
                (mm_evt_paylod_set_get_stream_parms_t *)in_val;
            rc = mm_channel_get_stream_parm(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_DO_STREAM_ACTION:
        {
            mm_evt_paylod_do_stream_action_t *payload =
                (mm_evt_paylod_do_stream_action_t *)in_val;
            rc = mm_channel_do_stream_action(my_obj, payload);
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUF:
        {
            cam_buf_map_type *payload =
                (cam_buf_map_type *)in_val;
            if (payload != NULL) {
                uint8_t type = payload->type;
                if ((type == CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF) ||
                        (type == CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF)) {
                    rc = mm_channel_map_stream_buf(my_obj, payload);
                }
            } else {
                LOGE("cannot map regualr stream buf in active state");
            }
        }
        break;
    case MM_CHANNEL_EVT_MAP_STREAM_BUFS:
        {
            cam_buf_map_type_list *payload =
                (cam_buf_map_type_list *)in_val;
            if ((payload != NULL) && (payload->length > 0)) {
                uint8_t type = payload->buf_maps[0].type;
                if ((type == CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF) ||
                        (type == CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF)) {
                    rc = mm_channel_map_stream_bufs(my_obj, payload);
                }
            } else {
                LOGE("cannot map regualr stream buf in active state");
            }
        }
        break;
    case MM_CHANNEL_EVT_UNMAP_STREAM_BUF:
        {
            cam_buf_unmap_type *payload =
                (cam_buf_unmap_type *)in_val;
            if (payload != NULL) {
                uint8_t type = payload->type;
                if ((type == CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF) ||
                        (type == CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF)) {
                    rc = mm_channel_unmap_stream_buf(my_obj, payload);
                }
            } else {
                LOGE("cannot unmap regualr stream buf in active state");
            }
        }
        break;
    case MM_CHANNEL_EVT_AF_BRACKETING:
        {
            LOGH("MM_CHANNEL_EVT_AF_BRACKETING");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_AF_BRACKETING;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_AE_BRACKETING:
        {
            LOGH("MM_CHANNEL_EVT_AE_BRACKETING");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_AE_BRACKETING;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_FLASH_BRACKETING:
        {
            LOGH("MM_CHANNEL_EVT_FLASH_BRACKETING");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_FLASH_BRACKETING;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_ZOOM_1X:
        {
            LOGH("MM_CHANNEL_EVT_ZOOM_1X");
            uint32_t start_flag = *((uint32_t *)in_val);
            mm_camera_generic_cmd_t gen_cmd;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_ZOOM_1X;
            gen_cmd.payload[0] = start_flag;
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CAMERA_EVT_CAPTURE_SETTING:
        {
            mm_camera_generic_cmd_t gen_cmd;
            cam_capture_frame_config_t *input;
            gen_cmd.type = MM_CAMERA_GENERIC_CMD_TYPE_CAPTURE_SETTING;
            LOGH("MM_CAMERA_EVT_CAPTURE_SETTING");
            if (in_val == NULL) {
                gen_cmd.payload[0] = 0;
                memset(&gen_cmd.frame_config, 0, sizeof(cam_capture_frame_config_t));
            } else {
                gen_cmd.payload[0] = 1;
                input = (cam_capture_frame_config_t *)in_val;
                gen_cmd.frame_config = *input;
            }
            rc = mm_channel_proc_general_cmd(my_obj, &gen_cmd);
        }
        break;
    case MM_CHANNEL_EVT_REG_STREAM_BUF_CB:
        {
            mm_evt_paylod_reg_stream_buf_cb *payload =
                (mm_evt_paylod_reg_stream_buf_cb *)in_val;
            rc = mm_channel_reg_stream_buf_cb (my_obj,
                    payload->stream_id, payload->buf_cb);
        }
        break;
     default:
        LOGE("invalid state (%d) for evt (%d), in(%p), out(%p)",
                    my_obj->state, evt, in_val, out_val);
        break;
    }
    LOGD("X rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_fsm_fn_paused
 *
 * DESCRIPTION: channel finite state machine function to handle event
 *              in PAUSED state.
 *
 * PARAMETERS :
 *   @my_obj   : ptr to a channel object
 *   @evt      : channel event to be processed
 *   @in_val   : input event payload. Can be NULL if not needed.
 *   @out_val  : output payload, Can be NULL if not needed.
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_fsm_fn_paused(mm_channel_t *my_obj,
                          mm_channel_evt_type_t evt,
                          void * in_val,
                          void * out_val)
{
    int32_t rc = 0;

    /* currently we are not supporting pause/resume channel */
    LOGE("invalid state (%d) for evt (%d), in(%p), out(%p)",
                my_obj->state, evt, in_val, out_val);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_init
 *
 * DESCRIPTION: initialize a channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object be to initialized
 *   @attr         : bundle attribute of the channel if needed
 *   @channel_cb   : callback function for bundle data notify
 *   @userdata     : user data ptr
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : if no bundle data notify is needed, meaning each stream in the
 *              channel will have its own stream data notify callback, then
 *              attr, channel_cb, and userdata can be NULL. In this case,
 *              no matching logic will be performed in channel for the bundling.
 *==========================================================================*/
int32_t mm_channel_init(mm_channel_t *my_obj,
                        mm_camera_channel_attr_t *attr,
                        mm_camera_buf_notify_t channel_cb,
                        void *userdata)
{
    int32_t rc = 0;

    my_obj->bundle.super_buf_notify_cb = channel_cb;
    my_obj->bundle.user_data = userdata;
    if (NULL != attr) {
        my_obj->bundle.superbuf_queue.attr = *attr;
    }

    LOGD("Launch data poll thread in channel open");
    snprintf(my_obj->poll_thread[0].threadName, THREAD_NAME_SIZE, "CAM_dataPoll");
    mm_camera_poll_thread_launch(&my_obj->poll_thread[0],
                                 MM_CAMERA_POLL_TYPE_DATA);

    /* change state to stopped state */
    my_obj->state = MM_CHANNEL_STATE_STOPPED;
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_release
 *
 * DESCRIPTION: release a channel resource. Channel state will move to UNUSED
 *              state after this call.
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : none
 *==========================================================================*/
void mm_channel_release(mm_channel_t *my_obj)
{
    /* stop data poll thread */
    mm_camera_poll_thread_release(&my_obj->poll_thread[0]);

    /* memset bundle info */
    memset(&my_obj->bundle, 0, sizeof(mm_channel_bundle_t));

    /* change state to notused state */
    my_obj->state = MM_CHANNEL_STATE_NOTUSED;
}

/*===========================================================================
 * FUNCTION   : mm_channel_link_stream
 *
 * DESCRIPTION: link a stream from external channel into this channel
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @stream_link  : channel and stream to be linked
 *
 * RETURN     : uint32_t type of stream handle
 *              0  -- invalid stream handle, meaning the op failed
 *              >0 -- successfully added a stream with a valid handle
 *==========================================================================*/
uint32_t mm_channel_link_stream(mm_channel_t *my_obj,
        mm_camera_stream_link_t *stream_link)
{
    uint8_t idx = 0;
    uint32_t s_hdl = 0;
    mm_stream_t *stream_obj = NULL;
    mm_stream_t *stream = NULL;

    if (NULL == stream_link) {
        LOGE("Invalid stream link");
        return 0;
    }

    stream = mm_channel_util_get_stream_by_handler(stream_link->ch,
            stream_link->stream_id);
    if (NULL == stream) {
        return 0;
    }

    /* check available stream */
    for (idx = 0; idx < MAX_STREAM_NUM_IN_BUNDLE; idx++) {
        if (MM_STREAM_STATE_NOTUSED == my_obj->streams[idx].state) {
            stream_obj = &my_obj->streams[idx];
            break;
        }
    }
    if (NULL == stream_obj) {
        LOGE("streams reach max, no more stream allowed to add");
        return s_hdl;
    }

    /* initialize stream object */
    *stream_obj = *stream;
    stream_obj->linked_stream = stream;
    s_hdl = stream->my_hdl;

    LOGD("stream handle = %d", s_hdl);
    return s_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_channel_add_stream
 *
 * DESCRIPTION: add a stream into the channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : uint32_t type of stream handle
 *              0  -- invalid stream handle, meaning the op failed
 *              >0 -- successfully added a stream with a valid handle
 *==========================================================================*/
uint32_t mm_channel_add_stream(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    uint8_t idx = 0;
    uint32_t s_hdl = 0;
    mm_stream_t *stream_obj = NULL;

    LOGD("E");
    /* check available stream */
    for (idx = 0; idx < MAX_STREAM_NUM_IN_BUNDLE; idx++) {
        if (MM_STREAM_STATE_NOTUSED == my_obj->streams[idx].state) {
            stream_obj = &my_obj->streams[idx];
            break;
        }
    }
    if (NULL == stream_obj) {
        LOGE("streams reach max, no more stream allowed to add");
        return s_hdl;
    }
    pthread_condattr_t cond_attr;
    pthread_condattr_init(&cond_attr);
    pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
    /* initialize stream object */
    memset(stream_obj, 0, sizeof(mm_stream_t));
    stream_obj->fd = -1;
    stream_obj->my_hdl = mm_camera_util_generate_handler(idx);
    stream_obj->ch_obj = my_obj;
    pthread_mutex_init(&stream_obj->buf_lock, NULL);
    pthread_mutex_init(&stream_obj->cb_lock, NULL);
    pthread_mutex_init(&stream_obj->cmd_lock, NULL);
    pthread_cond_init(&stream_obj->buf_cond, &cond_attr);
    pthread_condattr_destroy(&cond_attr);
    memset(stream_obj->buf_status, 0,
            sizeof(stream_obj->buf_status));
    stream_obj->state = MM_STREAM_STATE_INITED;

    /* acquire stream */
    rc = mm_stream_fsm_fn(stream_obj, MM_STREAM_EVT_ACQUIRE, NULL, NULL);
    if (0 == rc) {
        s_hdl = stream_obj->my_hdl;
    } else {
        /* error during acquire, de-init */
        pthread_cond_destroy(&stream_obj->buf_cond);
        pthread_mutex_destroy(&stream_obj->buf_lock);
        pthread_mutex_destroy(&stream_obj->cb_lock);
        pthread_mutex_destroy(&stream_obj->cmd_lock);
        memset(stream_obj, 0, sizeof(mm_stream_t));
    }
    LOGD("stream handle = %d", s_hdl);
    return s_hdl;
}

/*===========================================================================
 * FUNCTION   : mm_channel_del_stream
 *
 * DESCRIPTION: delete a stream from the channel bu its handle
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @stream_id    : stream handle
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : assume steam is stooped before it can be deleted
 *==========================================================================*/
int32_t mm_channel_del_stream(mm_channel_t *my_obj,
                              uint32_t stream_id)
{
    int rc = -1;
    mm_stream_t * stream_obj = NULL;
    stream_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL == stream_obj) {
        LOGE("Invalid Stream Object for stream_id = %d", stream_id);
        return rc;
    }

    if (stream_obj->ch_obj != my_obj) {
        /* Only unlink stream */
        pthread_mutex_lock(&stream_obj->linked_stream->buf_lock);
        stream_obj->linked_stream->is_linked = 0;
        stream_obj->linked_stream->linked_obj = NULL;
        pthread_mutex_unlock(&stream_obj->linked_stream->buf_lock);
        memset(stream_obj, 0, sizeof(mm_stream_t));

        return 0;
    }

    rc = mm_stream_fsm_fn(stream_obj,
                          MM_STREAM_EVT_RELEASE,
                          NULL,
                          NULL);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_config_stream
 *
 * DESCRIPTION: configure a stream
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @stream_id    : stream handle
 *   @config       : stream configuration
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_config_stream(mm_channel_t *my_obj,
                                   uint32_t stream_id,
                                   mm_camera_stream_config_t *config)
{
    int rc = -1;
    mm_stream_t * stream_obj = NULL;
    LOGD("E stream ID = %d", stream_id);
    stream_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL == stream_obj) {
        LOGE("Invalid Stream Object for stream_id = %d", stream_id);
        return rc;
    }

    if (stream_obj->ch_obj != my_obj) {
        /* No op. on linked streams */
        return 0;
    }

    /* set stream fmt */
    rc = mm_stream_fsm_fn(stream_obj,
                          MM_STREAM_EVT_SET_FMT,
                          (void *)config,
                          NULL);
    LOGD("X rc = %d",rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_get_bundle_info
 *
 * DESCRIPTION: query bundle info of the channel, which should include all
 *              streams within this channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @bundle_info  : bundle info to be filled in
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_get_bundle_info(mm_channel_t *my_obj,
                                   cam_bundle_config_t *bundle_info)
{
    int i;
    mm_stream_t *s_obj = NULL;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;
    int32_t rc = 0;

    memset(bundle_info, 0, sizeof(cam_bundle_config_t));
    bundle_info->bundle_id = my_obj->my_hdl;
    bundle_info->num_of_streams = 0;
    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                stream_type = s_obj->stream_info->stream_type;
                if ((CAM_STREAM_TYPE_METADATA != stream_type) &&
                        (s_obj->ch_obj == my_obj)) {
                    bundle_info->stream_ids[bundle_info->num_of_streams++] =
                                                        s_obj->server_stream_id;
                }
            } else {
                LOGE("cannot find stream obj (%d) by handler (%d)",
                            i, my_obj->streams[i].my_hdl);
                rc = -1;
                break;
            }
        }
    }
    if (rc != 0) {
        /* error, reset to 0 */
        memset(bundle_info, 0, sizeof(cam_bundle_config_t));
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_start
 *
 * DESCRIPTION: start a channel, which will start all streams in the channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_start(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    int i = 0, j = 0;
    mm_stream_t *s_objs[MAX_STREAM_NUM_IN_BUNDLE] = {NULL};
    uint8_t num_streams_to_start = 0;
    uint8_t num_streams_in_bundle_queue = 0;
    mm_stream_t *s_obj = NULL;
    int meta_stream_idx = 0;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                stream_type = s_obj->stream_info->stream_type;
                /* remember meta data stream index */
                if ((stream_type == CAM_STREAM_TYPE_METADATA) &&
                        (s_obj->ch_obj == my_obj)) {
                    meta_stream_idx = num_streams_to_start;
                }
                s_objs[num_streams_to_start++] = s_obj;

                if (!s_obj->stream_info->noFrameExpected) {
                    num_streams_in_bundle_queue++;
                }
            }
        }
    }

    if (meta_stream_idx > 0 ) {
        /* always start meta data stream first, so switch the stream object with the first one */
        s_obj = s_objs[0];
        s_objs[0] = s_objs[meta_stream_idx];
        s_objs[meta_stream_idx] = s_obj;
    }

    if (NULL != my_obj->bundle.super_buf_notify_cb) {
        /* need to send up cb, therefore launch thread */
        /* init superbuf queue */
        mm_channel_superbuf_queue_init(&my_obj->bundle.superbuf_queue);
        my_obj->bundle.superbuf_queue.num_streams = num_streams_in_bundle_queue;
        my_obj->bundle.superbuf_queue.expected_frame_id =
                my_obj->bundle.superbuf_queue.attr.user_expected_frame_id;
        my_obj->bundle.superbuf_queue.expected_frame_id_without_led = 0;
        my_obj->bundle.superbuf_queue.led_off_start_frame_id = 0;
        my_obj->bundle.superbuf_queue.led_on_start_frame_id = 0;
        my_obj->bundle.superbuf_queue.led_on_num_frames = 0;
        my_obj->bundle.superbuf_queue.good_frame_id = 0;

        for (i = 0; i < num_streams_to_start; i++) {
            /* Only bundle streams that belong to the channel */
            if(!(s_objs[i]->stream_info->noFrameExpected)) {
                if (s_objs[i]->ch_obj == my_obj) {
                    /* set bundled flag to streams */
                    s_objs[i]->is_bundled = 1;
                }
                my_obj->bundle.superbuf_queue.bundled_streams[j++] = s_objs[i]->my_hdl;
            }
        }

        /* launch cb thread for dispatching super buf through cb */
        snprintf(my_obj->cb_thread.threadName, THREAD_NAME_SIZE, "CAM_SuperBuf");
        mm_camera_cmd_thread_launch(&my_obj->cb_thread,
                                    mm_channel_dispatch_super_buf,
                                    (void*)my_obj);

        /* launch cmd thread for super buf dataCB */
        snprintf(my_obj->cmd_thread.threadName, THREAD_NAME_SIZE, "CAM_SuperBufCB");
        mm_camera_cmd_thread_launch(&my_obj->cmd_thread,
                                    mm_channel_process_stream_buf,
                                    (void*)my_obj);

        /* set flag to TRUE */
        my_obj->bundle.is_active = TRUE;
    }

    /* link any streams first before starting the rest of the streams */
    for (i = 0; i < num_streams_to_start; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            pthread_mutex_lock(&s_objs[i]->linked_stream->buf_lock);
            s_objs[i]->linked_stream->linked_obj = my_obj;
            s_objs[i]->linked_stream->is_linked = 1;
            pthread_mutex_unlock(&s_objs[i]->linked_stream->buf_lock);
            continue;
        }
    }

    for (i = 0; i < num_streams_to_start; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            continue;
        }
        /* all streams within a channel should be started at the same time */
        if (s_objs[i]->state == MM_STREAM_STATE_ACTIVE) {
            LOGE("stream already started idx(%d)", i);
            rc = -1;
            break;
        }

        /* allocate buf */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_GET_BUF,
                              NULL,
                              NULL);
        if (0 != rc) {
            LOGE("get buf failed at idx(%d)", i);
            break;
        }

        /* reg buf */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_REG_BUF,
                              NULL,
                              NULL);
        if (0 != rc) {
            LOGE("reg buf failed at idx(%d)", i);
            break;
        }

        /* start stream */
        rc = mm_stream_fsm_fn(s_objs[i],
                              MM_STREAM_EVT_START,
                              NULL,
                              NULL);
        if (0 != rc) {
            LOGE("start stream failed at idx(%d)", i);
            break;
        }
    }

    /* error handling */
    if (0 != rc) {
        /* unlink the streams first */
        for (j = 0; j < num_streams_to_start; j++) {
            if (s_objs[j]->ch_obj != my_obj) {
                pthread_mutex_lock(&s_objs[j]->linked_stream->buf_lock);
                s_objs[j]->linked_stream->is_linked = 0;
                s_objs[j]->linked_stream->linked_obj = NULL;
                pthread_mutex_unlock(&s_objs[j]->linked_stream->buf_lock);

                if (TRUE == my_obj->bundle.is_active) {
                    mm_channel_flush_super_buf_queue(my_obj, 0,
                            s_objs[i]->stream_info->stream_type);
                }
                memset(s_objs[j], 0, sizeof(mm_stream_t));
                continue;
            }
        }

        for (j = 0; j <= i; j++) {
            if ((NULL == s_objs[j]) || (s_objs[j]->ch_obj != my_obj)) {
                continue;
            }
            /* stop streams*/
            mm_stream_fsm_fn(s_objs[j],
                             MM_STREAM_EVT_STOP,
                             NULL,
                             NULL);

            /* unreg buf */
            mm_stream_fsm_fn(s_objs[j],
                             MM_STREAM_EVT_UNREG_BUF,
                             NULL,
                             NULL);

            /* put buf back */
            mm_stream_fsm_fn(s_objs[j],
                             MM_STREAM_EVT_PUT_BUF,
                             NULL,
                             NULL);
        }

        /* destroy super buf cmd thread */
        if (TRUE == my_obj->bundle.is_active) {
            /* first stop bundle thread */
            mm_camera_cmd_thread_release(&my_obj->cmd_thread);
            mm_camera_cmd_thread_release(&my_obj->cb_thread);

            /* deinit superbuf queue */
            mm_channel_superbuf_queue_deinit(&my_obj->bundle.superbuf_queue);

            /* memset super buffer queue info */
            my_obj->bundle.is_active = 0;
            memset(&my_obj->bundle.superbuf_queue, 0, sizeof(mm_channel_queue_t));
        }
    }
    my_obj->bWaitForPrepSnapshotDone = 0;
    if (my_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
        LOGH("registering Channel obj %p", my_obj);
        mm_frame_sync_register_channel(my_obj);
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_stop
 *
 * DESCRIPTION: stop a channel, which will stop all streams in the channel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_stop(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    int i;
    mm_stream_t *s_objs[MAX_STREAM_NUM_IN_BUNDLE] = {NULL};
    uint8_t num_streams_to_stop = 0;
    mm_stream_t *s_obj = NULL;
    int meta_stream_idx = 0;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    if (my_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
        mm_frame_sync_unregister_channel(my_obj);
    }

    for (i = 0; i < MAX_STREAM_NUM_IN_BUNDLE; i++) {
        if (my_obj->streams[i].my_hdl > 0) {
            s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                          my_obj->streams[i].my_hdl);
            if (NULL != s_obj) {
                if (s_obj->ch_obj == my_obj) {
                    stream_type = s_obj->stream_info->stream_type;
                    /* remember meta data stream index */
                    if (stream_type == CAM_STREAM_TYPE_METADATA) {
                        meta_stream_idx = num_streams_to_stop;
                    }
                }
                s_objs[num_streams_to_stop++] = s_obj;
            }
        }
    }

    if (meta_stream_idx < num_streams_to_stop - 1 ) {
        /* always stop meta data stream last, so switch the stream object with the last one */
        s_obj = s_objs[num_streams_to_stop - 1];
        s_objs[num_streams_to_stop - 1] = s_objs[meta_stream_idx];
        s_objs[meta_stream_idx] = s_obj;
    }

    for (i = 0; i < num_streams_to_stop; i++) {
        /* stream that are linked to this channel should not be stopped */
        if (s_objs[i]->ch_obj != my_obj) {
            continue;
        }

        /* stream off */
        mm_stream_fsm_fn(s_objs[i],
                         MM_STREAM_EVT_STOP,
                         NULL,
                         NULL);

        /* unreg buf at kernel */
        mm_stream_fsm_fn(s_objs[i],
                         MM_STREAM_EVT_UNREG_BUF,
                         NULL,
                         NULL);
    }

    for (i = 0; i < num_streams_to_stop; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            /* Only unlink stream */
            pthread_mutex_lock(&s_objs[i]->linked_stream->buf_lock);
            s_objs[i]->linked_stream->is_linked = 0;
            s_objs[i]->linked_stream->linked_obj = NULL;
            pthread_mutex_unlock(&s_objs[i]->linked_stream->buf_lock);
        }
    }

    /* destroy super buf cmd thread */
    if (TRUE == my_obj->bundle.is_active) {
        mm_channel_flush_super_buf_queue(my_obj, 0, CAM_STREAM_TYPE_DEFAULT);
        /* first stop bundle thread */
        mm_camera_cmd_thread_release(&my_obj->cmd_thread);
        mm_camera_cmd_thread_release(&my_obj->cb_thread);

        /* deinit superbuf queue */
        mm_channel_superbuf_queue_deinit(&my_obj->bundle.superbuf_queue);

        /* reset few fields in the bundle info */
        my_obj->bundle.is_active = 0;
        my_obj->bundle.superbuf_queue.expected_frame_id = 0;
        my_obj->bundle.superbuf_queue.good_frame_id = 0;
        my_obj->bundle.superbuf_queue.match_cnt = 0;
    }

    /* since all streams are stopped, we are safe to
     * release all buffers allocated in stream */
    for (i = 0; i < num_streams_to_stop; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            continue;
        }
        /* put buf back */
        mm_stream_fsm_fn(s_objs[i],
                         MM_STREAM_EVT_PUT_BUF,
                         NULL,
                         NULL);
    }

    for (i = 0; i < num_streams_to_stop; i++) {
        if (s_objs[i]->ch_obj != my_obj) {
            memset(s_objs[i], 0, sizeof(mm_stream_t));
        } else {
            continue;
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_request_super_buf
 *
 * DESCRIPTION: for burst mode in bundle, reuqest certain amount of matched
 *              frames from superbuf queue
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @num_buf_requested : number of matched frames needed
 *   @num_retro_buf_requested : number of retro frames needed
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_request_super_buf(mm_channel_t *my_obj,
        mm_camera_req_buf_t *buf)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    if(!buf) {
        LOGE("Request info buf is NULL");
        return -1;
    }

    /* set pending_cnt
     * will trigger dispatching super frames if pending_cnt > 0 */
    /* send cam_sem_post to wake up cmd thread to dispatch super buffer */
    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->cmd_type = MM_CAMERA_CMD_TYPE_REQ_DATA_CB;
        node->u.req_buf = *buf;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_cancel_super_buf_request
 *
 * DESCRIPTION: for burst mode in bundle, cancel the reuqest for certain amount
 *              of matched frames from superbuf queue
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_cancel_super_buf_request(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    /* reset pending_cnt */
    mm_camera_req_buf_t buf;
    memset(&buf, 0x0, sizeof(buf));
    buf.type = MM_CAMERA_REQ_SUPER_BUF;
    buf.num_buf_requested = 0;
    rc = mm_channel_request_super_buf(my_obj, &buf);
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_flush_super_buf_queue
 *
 * DESCRIPTION: flush superbuf queue
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @frame_idx : frame idx until which to flush all superbufs
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_flush_super_buf_queue(mm_channel_t *my_obj, uint32_t frame_idx,
                                                     cam_stream_type_t stream_type)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->cmd_type = MM_CAMERA_CMD_TYPE_FLUSH_QUEUE;
        node->u.flush_cmd.frame_idx = frame_idx;
        node->u.flush_cmd.stream_type = stream_type;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));

        /* wait for ack from cmd thread */
        cam_sem_wait(&(my_obj->cmd_thread.sync_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_config_notify_mode
 *
 * DESCRIPTION: configure notification mode
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @notify_mode : notification mode
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_config_notify_mode(mm_channel_t *my_obj,
                                      mm_camera_super_buf_notify_mode_t notify_mode)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->u.notify_mode = notify_mode;
        node->cmd_type = MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_start_zsl_snapshot
 *
 * DESCRIPTION: start zsl snapshot
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_start_zsl_snapshot(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->cmd_type = MM_CAMERA_CMD_TYPE_START_ZSL;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_stop_zsl_snapshot
 *
 * DESCRIPTION: stop zsl snapshot
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_stop_zsl_snapshot(mm_channel_t *my_obj)
{
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->cmd_type = MM_CAMERA_CMD_TYPE_STOP_ZSL;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_qbuf
 *
 * DESCRIPTION: enqueue buffer back to kernel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @buf          : buf ptr to be enqueued
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_qbuf(mm_channel_t *my_obj,
                        mm_camera_buf_def_t *buf)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj, buf->stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* Redirect to linked stream */
            rc = mm_stream_fsm_fn(s_obj->linked_stream,
                    MM_STREAM_EVT_QBUF,
                    (void *)buf,
                    NULL);
        } else {
            rc = mm_stream_fsm_fn(s_obj,
                    MM_STREAM_EVT_QBUF,
                    (void *)buf,
                    NULL);
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_cancel_buf
 *
 * DESCRIPTION: Get back buffer already sent to kernel
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @buf          : buf ptr to be enqueued
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_cancel_buf(mm_channel_t *my_obj,
                        uint32_t stream_id, uint32_t buf_idx)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* Redirect to linked stream */
            rc = mm_stream_fsm_fn(s_obj->linked_stream,
                    MM_STREAM_EVT_CANCEL_BUF,
                    (void *)&buf_idx,
                    NULL);
        } else {
            rc = mm_stream_fsm_fn(s_obj,
                    MM_STREAM_EVT_CANCEL_BUF,
                    (void *)&buf_idx,
                    NULL);
        }
    }

    return rc;
}


/*===========================================================================
 * FUNCTION   : mm_channel_get_queued_buf_count
 *
 * DESCRIPTION: return queued buffer count
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @stream_id    : steam_id
 *
 * RETURN     : queued buffer count
 *==========================================================================*/
int32_t mm_channel_get_queued_buf_count(mm_channel_t *my_obj, uint32_t stream_id)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj, stream_id);

    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* Redirect to linked stream */
            rc = mm_stream_fsm_fn(s_obj->linked_stream,
                    MM_STREAM_EVT_GET_QUEUED_BUF_COUNT,
                    NULL,
                    NULL);
        } else {
            rc = mm_stream_fsm_fn(s_obj,
                    MM_STREAM_EVT_GET_QUEUED_BUF_COUNT,
                    NULL,
                    NULL);
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_set_stream_parms
 *
 * DESCRIPTION: set parameters per stream
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @s_id         : stream handle
 *   @parms        : ptr to a param struct to be set to server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the parms struct buf is already mapped to server via
 *              domain socket. Corresponding fields of parameters to be set
 *              are already filled in by upper layer caller.
 *==========================================================================*/
int32_t mm_channel_set_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_fsm_fn(s_obj,
                              MM_STREAM_EVT_SET_PARM,
                              (void *)payload,
                              NULL);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_get_stream_parms
 *
 * DESCRIPTION: get parameters per stream
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @s_id         : stream handle
 *   @parms        : ptr to a param struct to be get from server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the parms struct buf is already mapped to server via
 *              domain socket. Parameters to be get from server are already
 *              filled in by upper layer caller. After this call, corresponding
 *              fields of requested parameters will be filled in by server with
 *              detailed information.
 *==========================================================================*/
int32_t mm_channel_get_stream_parm(mm_channel_t *my_obj,
                                   mm_evt_paylod_set_get_stream_parms_t *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_fsm_fn(s_obj,
                              MM_STREAM_EVT_GET_PARM,
                              (void *)payload,
                              NULL);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_do_stream_action
 *
 * DESCRIPTION: request server to perform stream based action. Maybe removed later
 *              if the functionality is included in mm_camera_set_parms
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @s_id         : stream handle
 *   @actions      : ptr to an action struct buf to be performed by server
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 * NOTE       : Assume the action struct buf is already mapped to server via
 *              domain socket. Actions to be performed by server are already
 *              filled in by upper layer caller.
 *==========================================================================*/
int32_t mm_channel_do_stream_action(mm_channel_t *my_obj,
                                   mm_evt_paylod_do_stream_action_t *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_fsm_fn(s_obj,
                              MM_STREAM_EVT_DO_ACTION,
                              (void *)payload,
                              NULL);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_map_stream_buf
 *
 * DESCRIPTION: mapping stream buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @payload      : ptr to payload for mapping
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_map_stream_buf(mm_channel_t *my_obj,
                                  cam_buf_map_type *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
            payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }
        rc = mm_stream_map_buf(s_obj,
                payload->type, payload->frame_idx,
                payload->plane_idx, payload->fd,
                payload->size, payload->buffer);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_map_stream_bufs
 *
 * DESCRIPTION: mapping stream buffers via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @payload      : ptr to payload for mapping
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_map_stream_bufs(mm_channel_t *my_obj,
                                   cam_buf_map_type_list *payload)
{
    int32_t rc = -1;
    if ((payload == NULL) || (payload->length == 0)) {
        return rc;
    }

    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->buf_maps[0].stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_map_bufs(s_obj, payload);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_unmap_stream_buf
 *
 * DESCRIPTION: unmapping stream buffer via domain socket to server
 *
 * PARAMETERS :
 *   @my_obj       : channel object
 *   @payload      : ptr to unmap payload
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_unmap_stream_buf(mm_channel_t *my_obj,
                                    cam_buf_unmap_type *payload)
{
    int32_t rc = -1;
    mm_stream_t* s_obj = mm_channel_util_get_stream_by_handler(my_obj,
                                                               payload->stream_id);
    if (NULL != s_obj) {
        if (s_obj->ch_obj != my_obj) {
            /* No op. on linked streams */
            return 0;
        }

        rc = mm_stream_unmap_buf(s_obj, payload->type,
                                 payload->frame_idx, payload->plane_idx);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_queue_init
 *
 * DESCRIPTION: initialize superbuf queue in the channel
 *
 * PARAMETERS :
 *   @queue   : ptr to superbuf queue to be initialized
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_queue_init(mm_channel_queue_t * queue)
{
    return cam_queue_init(&queue->que);
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_queue_deinit
 *
 * DESCRIPTION: deinitialize superbuf queue in the channel
 *
 * PARAMETERS :
 *   @queue   : ptr to superbuf queue to be deinitialized
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_queue_deinit(mm_channel_queue_t * queue)
{
    return cam_queue_deinit(&queue->que);
}

/*===========================================================================
 * FUNCTION   : mm_channel_util_seq_comp_w_rollover
 *
 * DESCRIPTION: utility function to handle sequence number comparison with rollover
 *
 * PARAMETERS :
 *   @v1      : first value to be compared
 *   @v2      : second value to be compared
 *
 * RETURN     : int8_t type of comparison result
 *              >0  -- v1 larger than v2
 *              =0  -- vi equal to v2
 *              <0  -- v1 smaller than v2
 *==========================================================================*/
int8_t mm_channel_util_seq_comp_w_rollover(uint32_t v1,
                                           uint32_t v2)
{
    int8_t ret = 0;

    /* TODO: need to handle the case if v2 roll over to 0 */
    if (v1 > v2) {
        ret = 1;
    } else if (v1 < v2) {
        ret = -1;
    }

    return ret;
}

/*===========================================================================
 * FUNCTION   : mm_channel_validate_super_buf.
 *
 * DESCRIPTION: Validate incoming buffer with existing super buffer.
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *   @queue   : superbuf queue
 *   @buf_info: new buffer from stream
 *
 * RETURN     : int8_t type of validation result
 *              >0  -- Valid frame
 *              =0  -- Cannot validate
 *              <0  -- Invalid frame. Can be freed
 *==========================================================================*/
int8_t mm_channel_validate_super_buf(mm_channel_t* ch_obj,
        mm_channel_queue_t *queue, mm_camera_buf_info_t *buf_info)
{
    int8_t ret = 0;
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;

    (void)ch_obj;

    /* comp */
    pthread_mutex_lock(&queue->que.lock);
    head = &queue->que.head.list;
    /* get the last one in the queue which is possibly having no matching */
    pos = head->next;
    while (pos != head) {
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;
        if (NULL != super_buf) {
            if ((super_buf->expected_frame) &&
                    (buf_info->frame_idx == super_buf->frame_idx)) {
                //This is good frame. Expecting more frames. Keeping this frame.
                ret = 1;
                break;
            } else {
                pos = pos->next;
                continue;
            }
        }
    }
    pthread_mutex_unlock(&queue->que.lock);
    return ret;
}

/*===========================================================================
 * FUNCTION   : mm_channel_handle_metadata
 *
 * DESCRIPTION: Handle frame matching logic change due to metadata
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *   @queue   : superbuf queue
 *   @buf_info: new buffer from stream
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_handle_metadata(
                        mm_channel_t* ch_obj,
                        mm_channel_queue_t * queue,
                        mm_camera_buf_info_t *buf_info)
{

    int rc = 0 ;
    mm_stream_t* stream_obj = NULL;
    stream_obj = mm_channel_util_get_stream_by_handler(ch_obj,
                buf_info->stream_id);
    uint8_t is_prep_snapshot_done_valid = 0;
    uint8_t is_good_frame_idx_range_valid = 0;
    int32_t prep_snapshot_done_state = 0;
    cam_frame_idx_range_t good_frame_idx_range;
    uint8_t is_crop_1x_found = 0;
    uint32_t snapshot_stream_id = 0;
    uint32_t i;
    /* Set expected frame id to a future frame idx, large enough to wait
    * for good_frame_idx_range, and small enough to still capture an image */
    const uint32_t max_future_frame_offset = MM_CAMERA_MAX_FUTURE_FRAME_WAIT;

    memset(&good_frame_idx_range, 0, sizeof(good_frame_idx_range));

    if (NULL == stream_obj) {
        LOGE("Invalid Stream Object for stream_id = %d",
                    buf_info->stream_id);
        rc = -1;
        goto end;
    }
    if (NULL == stream_obj->stream_info) {
        LOGE("NULL stream info for stream_id = %d",
                     buf_info->stream_id);
        rc = -1;
        goto end;
    }

    if ((CAM_STREAM_TYPE_METADATA == stream_obj->stream_info->stream_type) &&
            ((stream_obj->ch_obj == ch_obj) ||
            ((stream_obj->linked_stream != NULL) &&
            (stream_obj->linked_stream->linked_obj == ch_obj)))) {
        const metadata_buffer_t *metadata;
        metadata = (const metadata_buffer_t *)buf_info->buf->buffer;

        if (NULL == metadata) {
            LOGE("NULL metadata buffer for metadata stream");
            rc = -1;
            goto end;
        }
        LOGL("E , expected frame id: %d", queue->expected_frame_id);

        IF_META_AVAILABLE(const int32_t, p_prep_snapshot_done_state,
                CAM_INTF_META_PREP_SNAPSHOT_DONE, metadata) {
            prep_snapshot_done_state = *p_prep_snapshot_done_state;
            is_prep_snapshot_done_valid = 1;
            LOGH("prepare snapshot done valid ");
        }
        IF_META_AVAILABLE(const cam_frame_idx_range_t, p_good_frame_idx_range,
                CAM_INTF_META_GOOD_FRAME_IDX_RANGE, metadata) {
            good_frame_idx_range = *p_good_frame_idx_range;
            is_good_frame_idx_range_valid = 1;
            LOGH("good_frame_idx_range : min: %d, max: %d , num frames = %d",
                 good_frame_idx_range.min_frame_idx,
                good_frame_idx_range.max_frame_idx, good_frame_idx_range.num_led_on_frames);
        }
        IF_META_AVAILABLE(const cam_crop_data_t, p_crop_data,
                CAM_INTF_META_CROP_DATA, metadata) {
            cam_crop_data_t crop_data = *p_crop_data;

            for (i = 0; i < ARRAY_SIZE(ch_obj->streams); i++) {
                if (MM_STREAM_STATE_NOTUSED == ch_obj->streams[i].state) {
                    continue;
                }
                if (CAM_STREAM_TYPE_SNAPSHOT ==
                    ch_obj->streams[i].stream_info->stream_type) {
                    snapshot_stream_id = ch_obj->streams[i].server_stream_id;
                    break;
                }
            }

            for (i=0; i<crop_data.num_of_streams; i++) {
                if (snapshot_stream_id == crop_data.crop_info[i].stream_id) {
                    if (!crop_data.crop_info[i].crop.left &&
                            !crop_data.crop_info[i].crop.top) {
                        is_crop_1x_found = 1;
                        break;
                    }
                }
            }
        }

        IF_META_AVAILABLE(const cam_buf_divert_info_t, p_divert_info,
                CAM_INTF_BUF_DIVERT_INFO, metadata) {
            cam_buf_divert_info_t divert_info = *p_divert_info;
            if (divert_info.frame_id >= buf_info->frame_idx) {
                ch_obj->diverted_frame_id = divert_info.frame_id;
            } else {
                ch_obj->diverted_frame_id = 0;
            }
        }

        if (ch_obj->isZoom1xFrameRequested) {
            if (is_crop_1x_found) {
                ch_obj->isZoom1xFrameRequested = 0;
                queue->expected_frame_id = buf_info->frame_idx + 1;
            } else {
                queue->expected_frame_id += max_future_frame_offset;
                /* Flush unwanted frames */
                mm_channel_superbuf_flush_matched(ch_obj, queue);
            }
            goto end;
        }

        if (ch_obj->startZSlSnapshotCalled && is_good_frame_idx_range_valid) {
            LOGI("frameID = %d, expected = %d good_frame_idx = %d",
                    buf_info->frame_idx, queue->expected_frame_id,
                    good_frame_idx_range.min_frame_idx);
        }

        if (is_prep_snapshot_done_valid) {
            ch_obj->bWaitForPrepSnapshotDone = 0;
            if (prep_snapshot_done_state == NEED_FUTURE_FRAME) {
                queue->expected_frame_id += max_future_frame_offset;
                LOGI("PreFlash Done. Need Main Flash");

                mm_channel_superbuf_flush(ch_obj,
                        queue, CAM_STREAM_TYPE_DEFAULT);

                ch_obj->needLEDFlash = TRUE;
            } else {
                ch_obj->needLEDFlash = FALSE;
            }
        }
        if (is_good_frame_idx_range_valid) {
            queue->expected_frame_id =
                good_frame_idx_range.min_frame_idx;
            queue->good_frame_id = good_frame_idx_range.min_frame_idx;
            if((ch_obj->needLEDFlash == TRUE) && (ch_obj->burstSnapNum > 1)) {
                queue->led_on_start_frame_id =
                good_frame_idx_range.min_frame_idx;
                queue->led_off_start_frame_id =
                good_frame_idx_range.max_frame_idx;
                queue->once = 0;
                queue->led_on_num_frames =
                  good_frame_idx_range.num_led_on_frames;
                queue->frame_skip_count = good_frame_idx_range.frame_skip_count;
                LOGD("Need Flash, expected frame id = %d,"
                        " led_on start = %d, led off start = %d, led on frames = %d ",
                           queue->expected_frame_id, queue->led_on_start_frame_id,
                        queue->led_off_start_frame_id, queue->led_on_num_frames);
            } else {
                LOGD("No flash, expected frame id = %d ",
                         queue->expected_frame_id);
            }
        } else if ((MM_CHANNEL_BRACKETING_STATE_WAIT_GOOD_FRAME_IDX == ch_obj->bracketingState) &&
                !is_prep_snapshot_done_valid) {
            /* Flush unwanted frames */
            mm_channel_superbuf_flush_matched(ch_obj, queue);
            queue->expected_frame_id += max_future_frame_offset;
        }
        if (ch_obj->isFlashBracketingEnabled &&
            is_good_frame_idx_range_valid) {
            /* Flash bracketing needs two frames, with & without led flash.
            * in valid range min frame is with led flash and max frame is
            * without led flash */
            queue->expected_frame_id =
                    good_frame_idx_range.min_frame_idx;
            /* max frame is without led flash */
            queue->expected_frame_id_without_led =
                    good_frame_idx_range.max_frame_idx;
            queue->good_frame_id =
                    good_frame_idx_range.min_frame_idx;
        } else if (is_good_frame_idx_range_valid) {
            queue->expected_frame_id =
                    good_frame_idx_range.min_frame_idx;
            ch_obj->bracketingState = MM_CHANNEL_BRACKETING_STATE_ACTIVE;
            queue->good_frame_id =
                    good_frame_idx_range.min_frame_idx;
        }

        if (ch_obj->isConfigCapture && is_good_frame_idx_range_valid
                && (good_frame_idx_range.config_batch_idx < ch_obj->frameConfig.num_batch)) {

            LOGI("Frame Config: Expcted ID = %d batch index = %d",
                    good_frame_idx_range.min_frame_idx, good_frame_idx_range.config_batch_idx);
            ch_obj->capture_frame_id[good_frame_idx_range.config_batch_idx] =
                    good_frame_idx_range.min_frame_idx;

            if (ch_obj->cur_capture_idx == good_frame_idx_range.config_batch_idx) {
                queue->expected_frame_id =
                        good_frame_idx_range.min_frame_idx;
            } else {
                queue->expected_frame_id =
                        ch_obj->capture_frame_id[ch_obj->cur_capture_idx];
            }
            queue->good_frame_id = queue->expected_frame_id;
        }

        if ((ch_obj->burstSnapNum > 1) && (ch_obj->needLEDFlash == TRUE)
            && !ch_obj->isFlashBracketingEnabled
            && (MM_CHANNEL_BRACKETING_STATE_OFF == ch_obj->bracketingState)
            && !ch_obj->isConfigCapture) {
            if((buf_info->frame_idx >= queue->led_off_start_frame_id)
                    &&  !queue->once) {
                LOGD("Burst snap num = %d ",
                         ch_obj->burstSnapNum);
                // Skip frames from LED OFF frame to get a good frame
                queue->expected_frame_id = queue->led_off_start_frame_id +
                        queue->frame_skip_count;
                queue->once = 1;
                ch_obj->stopZslSnapshot = 1;
                ch_obj->needLEDFlash = FALSE;
                LOGD("Reached max led on frames = %d , expected id = %d",
                         buf_info->frame_idx, queue->expected_frame_id);
         }
       }

        IF_META_AVAILABLE(const cam_low_light_mode_t, low_light_level,
            CAM_INTF_META_LOW_LIGHT, metadata) {
            ch_obj->needLowLightZSL = *low_light_level;
        }

        // For the instant capture case, if AEC settles before expected frame ID from user,
        // reset the expected frame ID to current frame index.
        if (queue->attr.user_expected_frame_id > 0) {
            if (queue->attr.user_expected_frame_id > buf_info->frame_idx) {
                IF_META_AVAILABLE(const cam_3a_params_t, ae_params,
                    CAM_INTF_META_AEC_INFO, metadata) {
                    if (ae_params->settled) {
                        queue->expected_frame_id = buf_info->frame_idx;
                        // Reset the expected frame ID from HAL to 0
                        queue->attr.user_expected_frame_id = 0;
                        LOGD("AEC settled, reset expected frame ID from user");
                    }
                }
            } else {
                 // Reset the expected frame ID from HAL to 0 after
                 // current frame index is greater than expected id.
                queue->attr.user_expected_frame_id = 0;
                LOGD("reset expected frame ID from user as it reached the bound");
            }
        }
    }
end:
    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_comp_and_enqueue
 *
 * DESCRIPTION: implementation for matching logic for superbuf
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *   @queue   : superbuf queue
 *   @buf_info: new buffer from stream
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_comp_and_enqueue(
                        mm_channel_t* ch_obj,
                        mm_channel_queue_t *queue,
                        mm_camera_buf_info_t *buf_info)
{
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;
    uint8_t buf_s_idx, i, found_super_buf, unmatched_bundles;
    struct cam_list *last_buf, *insert_before_buf, *last_buf_ptr;

    LOGD("E");

    for (buf_s_idx = 0; buf_s_idx < queue->num_streams; buf_s_idx++) {
        if (buf_info->stream_id == queue->bundled_streams[buf_s_idx]) {
            break;
        }
    }

    if (buf_s_idx == queue->num_streams) {
        LOGE("buf from stream (%d) not bundled", buf_info->stream_id);
        return -1;
    }

    if(buf_info->frame_idx == 0) {
        mm_channel_qbuf(ch_obj, buf_info->buf);
        return 0;
    }

    if (mm_channel_handle_metadata(ch_obj, queue, buf_info) < 0) {
        mm_channel_qbuf(ch_obj, buf_info->buf);
        return -1;
    }

    if ((mm_channel_util_seq_comp_w_rollover(buf_info->frame_idx,
            queue->expected_frame_id) < 0) &&
            (mm_channel_validate_super_buf(ch_obj, queue, buf_info) <= 0)) {
        LOGH("incoming buf id(%d) is older than expected buf id(%d), will discard it",
                 buf_info->frame_idx, queue->expected_frame_id);
        mm_channel_qbuf(ch_obj, buf_info->buf);
        return 0;
    }

    /* comp */
    pthread_mutex_lock(&queue->que.lock);
    head = &queue->que.head.list;
    /* get the last one in the queue which is possibly having no matching */
    pos = head->next;

    found_super_buf = 0;
    unmatched_bundles = 0;
    last_buf = NULL;
    insert_before_buf = NULL;
    last_buf_ptr = NULL;

    while (pos != head) {
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;

        if (NULL != super_buf) {
            if (super_buf->matched) {
                /* find a matched super buf, move to next one */
                pos = pos->next;
                continue;
            } else if (( buf_info->frame_idx == super_buf->frame_idx )
                    /*Pick metadata greater than available frameID*/
                    || ((queue->attr.priority == MM_CAMERA_SUPER_BUF_PRIORITY_LOW)
                    && (super_buf->super_buf[buf_s_idx].frame_idx == 0)
                    && (buf_info->buf->stream_type == CAM_STREAM_TYPE_METADATA)
                    && (super_buf->frame_idx < buf_info->frame_idx))
                    /*Pick available metadata closest to frameID*/
                    || ((queue->attr.priority == MM_CAMERA_SUPER_BUF_PRIORITY_LOW)
                    && (buf_info->buf->stream_type != CAM_STREAM_TYPE_METADATA)
                    && (super_buf->super_buf[buf_s_idx].frame_idx == 0)
                    && (super_buf->unmatched_meta_idx > buf_info->frame_idx))){
                /*super buffer frame IDs matching OR In low priority bundling
                metadata frameID greater than avialbale super buffer frameID  OR
                metadata frame closest to incoming frameID will be bundled*/
                found_super_buf = 1;
                /* If we are filling into a 'meta only' superbuf, make sure to reset
                the super_buf frame_idx so that missing streams in this superbuf
                are filled as per matching frame id logic. Note that, in low priority
                queue, only meta frame id need not match (closest suffices) but
                the other streams in this superbuf should have same frame id. */
                if (super_buf->unmatched_meta_idx > 0) {
                    super_buf->unmatched_meta_idx = 0;
                    super_buf->frame_idx = buf_info->frame_idx;
                }
                break;
            } else {
                unmatched_bundles++;
                if ( NULL == last_buf ) {
                    if ( super_buf->frame_idx < buf_info->frame_idx ) {
                        last_buf = pos;
                    }
                }
                if ( NULL == insert_before_buf ) {
                    if ( super_buf->frame_idx > buf_info->frame_idx ) {
                        insert_before_buf = pos;
                    }
                }
                pos = pos->next;
            }
        }
    }

    if ( found_super_buf ) {
        if(super_buf->super_buf[buf_s_idx].frame_idx != 0) {
            //This can cause frame drop. We are overwriting same memory.
            pthread_mutex_unlock(&queue->que.lock);
            LOGW("Warning: frame is already in camera ZSL queue");
            mm_channel_qbuf(ch_obj, buf_info->buf);
            return 0;
        }

        /*Insert incoming buffer to super buffer*/
        super_buf->super_buf[buf_s_idx] = *buf_info;

        /* check if superbuf is all matched */
        super_buf->matched = 1;
        for (i=0; i < super_buf->num_of_bufs; i++) {
            if (super_buf->super_buf[i].frame_idx == 0) {
                super_buf->matched = 0;
                break;
            }
        }

        if (super_buf->matched) {
            if(ch_obj->isFlashBracketingEnabled) {
               queue->expected_frame_id =
                   queue->expected_frame_id_without_led;
               if (buf_info->frame_idx >=
                       queue->expected_frame_id_without_led) {
                   ch_obj->isFlashBracketingEnabled = FALSE;
               }
            } else {
               queue->expected_frame_id = buf_info->frame_idx
                                          + queue->attr.post_frame_skip;
            }

            super_buf->expected_frame = FALSE;

            LOGD("curr = %d, skip = %d , Expected Frame ID: %d",
                     buf_info->frame_idx,
                    queue->attr.post_frame_skip, queue->expected_frame_id);

            queue->match_cnt++;
            if (ch_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
                pthread_mutex_lock(&fs_lock);
                mm_frame_sync_add(buf_info->frame_idx, ch_obj);
                pthread_mutex_unlock(&fs_lock);
            }
            /* Any older unmatched buffer need to be released */
            if ( last_buf ) {
                while ( last_buf != pos ) {
                    node = member_of(last_buf, cam_node_t, list);
                    super_buf = (mm_channel_queue_node_t*)node->data;
                    if (NULL != super_buf) {
                        for (i=0; i<super_buf->num_of_bufs; i++) {
                            if (super_buf->super_buf[i].frame_idx != 0) {
                                mm_channel_qbuf(ch_obj, super_buf->super_buf[i].buf);
                            }
                        }
                        queue->que.size--;
                        last_buf = last_buf->next;
                        cam_list_del_node(&node->list);
                        free(node);
                        free(super_buf);
                    } else {
                        LOGE("Invalid superbuf in queue!");
                        break;
                    }
                }
            }
        }else {
            if (ch_obj->diverted_frame_id == buf_info->frame_idx) {
                super_buf->expected_frame = TRUE;
                ch_obj->diverted_frame_id = 0;
            }
        }
    } else {
        if ((queue->attr.max_unmatched_frames < unmatched_bundles)
                && ( NULL == last_buf )) {
            /* incoming frame is older than the last bundled one */
            mm_channel_qbuf(ch_obj, buf_info->buf);
        } else {
            last_buf_ptr = last_buf;

            /* Loop to remove unmatched frames */
            while ((queue->attr.max_unmatched_frames < unmatched_bundles)
                    && (last_buf_ptr != NULL && last_buf_ptr != pos)) {
                node = member_of(last_buf_ptr, cam_node_t, list);
                super_buf = (mm_channel_queue_node_t*)node->data;
                if (NULL != super_buf && super_buf->expected_frame == FALSE
                        && (&node->list != insert_before_buf)) {
                    for (i=0; i<super_buf->num_of_bufs; i++) {
                        if (super_buf->super_buf[i].frame_idx != 0) {
                            mm_channel_qbuf(ch_obj, super_buf->super_buf[i].buf);
                        }
                    }
                    queue->que.size--;
                    last_buf_ptr = last_buf_ptr->next;
                    cam_list_del_node(&node->list);
                    free(node);
                    free(super_buf);
                    unmatched_bundles--;
                } else {
                    last_buf_ptr = last_buf_ptr->next;
                }
            }

            if (queue->attr.max_unmatched_frames < unmatched_bundles) {
                node = member_of(last_buf, cam_node_t, list);
                super_buf = (mm_channel_queue_node_t*)node->data;
                for (i=0; i<super_buf->num_of_bufs; i++) {
                    if (super_buf->super_buf[i].frame_idx != 0) {
                        mm_channel_qbuf(ch_obj, super_buf->super_buf[i].buf);
                    }
                }
                queue->que.size--;
                cam_list_del_node(&node->list);
                free(node);
                free(super_buf);
            }

            /* insert the new frame at the appropriate position. */

            mm_channel_queue_node_t *new_buf = NULL;
            cam_node_t* new_node = NULL;

            new_buf = (mm_channel_queue_node_t*)malloc(sizeof(mm_channel_queue_node_t));
            new_node = (cam_node_t*)malloc(sizeof(cam_node_t));
            if (NULL != new_buf && NULL != new_node) {
                memset(new_buf, 0, sizeof(mm_channel_queue_node_t));
                memset(new_node, 0, sizeof(cam_node_t));
                new_node->data = (void *)new_buf;
                new_buf->num_of_bufs = queue->num_streams;
                new_buf->super_buf[buf_s_idx] = *buf_info;
                new_buf->frame_idx = buf_info->frame_idx;

                if ((ch_obj->diverted_frame_id == buf_info->frame_idx)
                        || (buf_info->frame_idx == queue->good_frame_id)) {
                    new_buf->expected_frame = TRUE;
                    ch_obj->diverted_frame_id = 0;
                }

                /* enqueue */
                if ( insert_before_buf ) {
                    cam_list_insert_before_node(&new_node->list, insert_before_buf);
                } else {
                    cam_list_add_tail_node(&new_node->list, &queue->que.head.list);
                }
                queue->que.size++;

                if(queue->num_streams == 1) {
                    new_buf->matched = 1;
                    new_buf->expected_frame = FALSE;
                    queue->expected_frame_id = buf_info->frame_idx + queue->attr.post_frame_skip;
                    queue->match_cnt++;
                    if (ch_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
                        pthread_mutex_lock(&fs_lock);
                        mm_frame_sync_add(buf_info->frame_idx, ch_obj);
                        pthread_mutex_unlock(&fs_lock);
                    }
                }
                /* In low priority queue, this will become a 'meta only' superbuf. Set the
                unmatched_frame_idx so that the upcoming stream buffers (other than meta)
                can be filled into this which are nearest to this idx. */
                if ((queue->attr.priority == MM_CAMERA_SUPER_BUF_PRIORITY_LOW)
                    && (buf_info->buf->stream_type == CAM_STREAM_TYPE_METADATA)) {
                    new_buf->unmatched_meta_idx = buf_info->frame_idx;
                }
            } else {
                /* No memory */
                if (NULL != new_buf) {
                    free(new_buf);
                }
                if (NULL != new_node) {
                    free(new_node);
                }
                /* qbuf the new buf since we cannot enqueue */
                mm_channel_qbuf(ch_obj, buf_info->buf);
            }
        }
    }

    pthread_mutex_unlock(&queue->que.lock);
    LOGD("X");
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_dequeue_internal
 *
 * DESCRIPTION: internal implementation for dequeue from the superbuf queue
 *
 * PARAMETERS :
 *   @queue   : superbuf queue
 *   @matched_only : if dequeued buf should be matched
 *   @ch_obj  : channel object
 *
 * RETURN     : ptr to a node from superbuf queue
 *==========================================================================*/
mm_channel_queue_node_t* mm_channel_superbuf_dequeue_internal(
        mm_channel_queue_t * queue,
        uint8_t matched_only, mm_channel_t *ch_obj)
{
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;

    head = &queue->que.head.list;
    pos = head->next;
    if (pos != head) {
        /* get the first node */
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;
        if ( (NULL != super_buf) &&
             (matched_only == TRUE) &&
             (super_buf->matched == FALSE) ) {
            /* require to dequeue matched frame only, but this superbuf is not matched,
               simply set return ptr to NULL */
            super_buf = NULL;
        }
        if (NULL != super_buf) {
            /* remove from the queue */
            cam_list_del_node(&node->list);
            queue->que.size--;
            if (super_buf->matched == TRUE) {
                queue->match_cnt--;
                if (ch_obj->bundle.superbuf_queue.attr.enable_frame_sync) {
                    pthread_mutex_lock(&fs_lock);
                    mm_frame_sync_remove(super_buf->frame_idx);
                    pthread_mutex_unlock(&fs_lock);
                }
            }
            free(node);
        }
    }

    return super_buf;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_dequeue_frame_internal
 *
 * DESCRIPTION: internal implementation for dequeue based on frame index
 *                     from the superbuf queue
 *
 * PARAMETERS :
 *   @queue       : superbuf queue
 *   @frame_idx  : frame index to be dequeued
 *
 * RETURN     : ptr to a node from superbuf queue with matched frame index
 *                : NULL if not found
 *==========================================================================*/
mm_channel_queue_node_t* mm_channel_superbuf_dequeue_frame_internal(
        mm_channel_queue_t * queue, uint32_t frame_idx)
{
    cam_node_t* node = NULL;
    struct cam_list *head = NULL;
    struct cam_list *pos = NULL;
    mm_channel_queue_node_t* super_buf = NULL;

    if (!queue) {
        LOGE("queue is NULL");
        return NULL;
    }

    head = &queue->que.head.list;
    pos = head->next;
    LOGL("Searching for match frame %d", frame_idx);
    while ((pos != head) && (pos != NULL)) {
        /* get the first node */
        node = member_of(pos, cam_node_t, list);
        super_buf = (mm_channel_queue_node_t*)node->data;
        if (super_buf && super_buf->matched &&
                (super_buf->frame_idx == frame_idx)) {
            /* remove from the queue */
            cam_list_del_node(&node->list);
            queue->que.size--;
            queue->match_cnt--;
            LOGH("Found match frame %d", frame_idx);
            free(node);
            break;
        }
        else {
            LOGH("match frame not found %d", frame_idx);
            super_buf = NULL;
        }
        pos = pos->next;
    }
    return super_buf;
}


/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_dequeue
 *
 * DESCRIPTION: dequeue from the superbuf queue
 *
 * PARAMETERS :
 *   @queue   : superbuf queue
 *   @ch_obj  : channel object
 *
 * RETURN     : ptr to a node from superbuf queue
 *==========================================================================*/
mm_channel_queue_node_t* mm_channel_superbuf_dequeue(
        mm_channel_queue_t * queue, mm_channel_t *ch_obj)
{
    mm_channel_queue_node_t* super_buf = NULL;

    pthread_mutex_lock(&queue->que.lock);
    super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, ch_obj);
    pthread_mutex_unlock(&queue->que.lock);

    return super_buf;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_bufdone_overflow
 *
 * DESCRIPTION: keep superbuf queue no larger than watermark set by upper layer
 *              via channel attribute
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_bufdone_overflow(mm_channel_t* my_obj,
                                             mm_channel_queue_t * queue)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;
    if (MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS == queue->attr.notify_mode) {
        /* for continuous streaming mode, no overflow is needed */
        return 0;
    }

    LOGD("before match_cnt=%d, water_mark=%d",
          queue->match_cnt, queue->attr.water_mark);
    /* bufdone overflowed bufs */
    pthread_mutex_lock(&queue->que.lock);
    while (queue->match_cnt > queue->attr.water_mark) {
        super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
        if (NULL != super_buf) {
            for (i=0; i<super_buf->num_of_bufs; i++) {
                if (NULL != super_buf->super_buf[i].buf) {
                    mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
                }
            }
            free(super_buf);
        }
    }
    pthread_mutex_unlock(&queue->que.lock);
    LOGD("after match_cnt=%d, water_mark=%d",
          queue->match_cnt, queue->attr.water_mark);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_skip
 *
 * DESCRIPTION: depends on the lookback configuration of the channel attribute,
 *              unwanted superbufs will be removed from the superbuf queue.
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_skip(mm_channel_t* my_obj,
                                 mm_channel_queue_t * queue)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;
    if (MM_CAMERA_SUPER_BUF_NOTIFY_CONTINUOUS == queue->attr.notify_mode) {
        /* for continuous streaming mode, no skip is needed */
        return 0;
    }

    /* bufdone overflowed bufs */
    pthread_mutex_lock(&queue->que.lock);
    while (queue->match_cnt > queue->attr.look_back) {
        super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
        if (NULL != super_buf) {
            for (i=0; i<super_buf->num_of_bufs; i++) {
                if (NULL != super_buf->super_buf[i].buf) {
                    mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
                }
            }
            free(super_buf);
        }
    }
    pthread_mutex_unlock(&queue->que.lock);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_flush
 *
 * DESCRIPTION: flush the superbuf queue.
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *   @cam_type: flush only particular type (default flushes all)
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_flush(mm_channel_t* my_obj,
        mm_channel_queue_t * queue, cam_stream_type_t cam_type)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;
    cam_stream_type_t stream_type = CAM_STREAM_TYPE_DEFAULT;

    /* bufdone bufs */
    pthread_mutex_lock(&queue->que.lock);
    super_buf = mm_channel_superbuf_dequeue_internal(queue, FALSE, my_obj);
    while (super_buf != NULL) {
        for (i=0; i<super_buf->num_of_bufs; i++) {
            if (NULL != super_buf->super_buf[i].buf) {
                stream_type = super_buf->super_buf[i].buf->stream_type;
                if ((CAM_STREAM_TYPE_DEFAULT == cam_type) ||
                        (cam_type == stream_type)) {
                    mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
                }
            }
        }
        free(super_buf);
        super_buf = mm_channel_superbuf_dequeue_internal(queue, FALSE, my_obj);
    }
    pthread_mutex_unlock(&queue->que.lock);

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_proc_general_cmd
 *
 * DESCRIPTION: process general command
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @notify_mode : notification mode
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_proc_general_cmd(mm_channel_t *my_obj,
                                      mm_camera_generic_cmd_t *p_gen_cmd)
{
    LOGD("E");
    int32_t rc = 0;
    mm_camera_cmdcb_t* node = NULL;

    node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
    if (NULL != node) {
        memset(node, 0, sizeof(mm_camera_cmdcb_t));
        node->u.gen_cmd = *p_gen_cmd;
        node->cmd_type = MM_CAMERA_CMD_TYPE_GENERAL;

        /* enqueue to cmd thread */
        cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);

        /* wake up cmd thread */
        cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
    } else {
        LOGE("No memory for mm_camera_node_t");
        rc = -1;
    }
    LOGD("X");

    return rc;
}

/*===========================================================================
 * FUNCTION   : mm_channel_superbuf_flush_matched
 *
 * DESCRIPTION: flush matched buffers from the superbuf queue.
 *
 * PARAMETERS :
 *   @my_obj  : channel object
 *   @queue   : superbuf queue
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_channel_superbuf_flush_matched(mm_channel_t* my_obj,
                                  mm_channel_queue_t * queue)
{
    int32_t rc = 0, i;
    mm_channel_queue_node_t* super_buf = NULL;

    /* bufdone bufs */
    pthread_mutex_lock(&queue->que.lock);
    super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
    while (super_buf != NULL) {
        for (i=0; i<super_buf->num_of_bufs; i++) {
            if (NULL != super_buf->super_buf[i].buf) {
                mm_channel_qbuf(my_obj, super_buf->super_buf[i].buf);
            }
        }
        free(super_buf);
        super_buf = mm_channel_superbuf_dequeue_internal(queue, TRUE, my_obj);
    }
    pthread_mutex_unlock(&queue->que.lock);

    return rc;
}


/*===========================================================================
 * FUNCTION   : mm_frame_sync_reset
 *
 * DESCRIPTION: Reset Frame sync info
 *
 * RETURN     : None
 *==========================================================================*/
void mm_frame_sync_reset() {
    memset(&fs, 0x0, sizeof(fs));
    LOGD("Reset Done");
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_register_channel
 *
 * DESCRIPTION: Register Channel for frame sync
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_register_channel(mm_channel_t *ch_obj) {
    // Lock frame sync info
    pthread_mutex_lock(&fs_lock);
    if ((fs.num_cam >= MAX_NUM_CAMERA_PER_BUNDLE) || (!ch_obj)) {
        LOGE("Error!! num cam(%d) is out of range ",
                 fs.num_cam);
        pthread_mutex_unlock(&fs_lock);
        return -1;
    }
    if (fs.num_cam == 0) {
        LOGH("First channel registering!!");
        mm_frame_sync_reset();
    }
    uint8_t i = 0;
    for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
        if (fs.ch_obj[i] == NULL) {
            fs.ch_obj[i] = ch_obj;
            fs.cb[i] = ch_obj->bundle.super_buf_notify_cb;
            fs.num_cam++;
            LOGD("DBG_FS index %d", i);
            break;
        }
    }
    if (i >= MAX_NUM_CAMERA_PER_BUNDLE) {
        LOGH("X, DBG_FS Cannot register channel!!");
        pthread_mutex_unlock(&fs_lock);
        return -1;
    }
    LOGH("num_cam %d ", fs.num_cam);
    pthread_mutex_unlock(&fs_lock);
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_unregister_channel
 *
 * DESCRIPTION: un-register Channel for frame sync
 *
 * PARAMETERS :
 *   @ch_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_unregister_channel(mm_channel_t *ch_obj) {
    uint8_t i = 0;
    // Lock frame sync info
    pthread_mutex_lock(&fs_lock);
    if (!fs.num_cam || !ch_obj) {
        LOGH("X, DBG_FS: channel not found  !!");
        // Lock frame sync info
        pthread_mutex_unlock(&fs_lock);
        return -1;
    }
    for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
        if (fs.ch_obj[i] == ch_obj) {
            LOGD("found ch_obj at i (%d) ", i);
            break;
        }
    }
    if (i < MAX_NUM_CAMERA_PER_BUNDLE) {
        LOGD("remove channel info ");
        fs.ch_obj[i] = NULL;
        fs.cb[i] = NULL;
        fs.num_cam--;
    } else {
        LOGD("DBG_FS Channel not found ");
    }
    if (fs.num_cam == 0) {
        mm_frame_sync_reset();
    }
    LOGH("X, fs.num_cam %d", fs.num_cam);
    pthread_mutex_unlock(&fs_lock);
    return 0;
}


/*===========================================================================
 * FUNCTION   : mm_frame_sync_add
 *
 * DESCRIPTION: Add frame info into frame sync nodes
 *
 * PARAMETERS :
 *   @frame_id  : frame id to be added
 *   @ch_obj  : channel object
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_add(uint32_t frame_id, mm_channel_t *ch_obj) {

    LOGD("E, frame id %d ch_obj %p", frame_id, ch_obj);
    if (!frame_id || !ch_obj) {
        LOGH("X : Error, cannot add sync frame !!");
        return -1;
    }

    int8_t ch_idx = -1;
    uint8_t i = 0;
    for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
        if (fs.ch_obj[i] == ch_obj) {
            ch_idx = i;
            LOGD("ch id %d ", ch_idx);
            break;
        }
    }
    if (ch_idx < 0) {
        LOGH("X : DBG_FS ch not found!!");
        return -1;
    }
    int8_t index = mm_frame_sync_find_frame_index(frame_id);
    if ((index >= 0) && (index < MM_CAMERA_FRAME_SYNC_NODES)) {
        fs.node[index].frame_valid[ch_idx] = 1;
    } else if (index < 0) {
        if (fs.pos >= MM_CAMERA_FRAME_SYNC_NODES) {
            fs.pos = 0;
        }
        index = fs.pos;
        memset(&fs.node[index], 0x00, sizeof(mm_channel_sync_node_t));
        fs.pos++;
        fs.node[index].frame_idx = frame_id;
        fs.node[index].frame_valid[ch_idx] = 1;
        if (fs.num_cam == 1) {
            LOGD("Single camera frame %d , matched ", frame_id);
            fs.node[index].matched = 1;
        }
    }
    uint8_t frames_valid = 0;
    if (!fs.node[index].matched) {
        for (i = 0; i < MAX_NUM_CAMERA_PER_BUNDLE; i++) {
            if (fs.node[index].frame_valid[i]) {
                frames_valid++;
            }
        }
        if (frames_valid == fs.num_cam) {
            fs.node[index].matched = 1;
            LOGD("dual camera frame %d , matched ",
                     frame_id);
        }
    }
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_remove
 *
 * DESCRIPTION: Remove frame info from frame sync nodes
 *
 * PARAMETERS :
 *   @frame_id  : frame id to be removed
 *
 * RETURN     : int32_t type of status
 *              0  -- success
 *              -1 -- failure
 *==========================================================================*/
int32_t mm_frame_sync_remove(uint32_t frame_id) {
    int8_t index = -1;

    LOGD("E, frame_id %d", frame_id);
    if (!frame_id) {
        LOGE("X, DBG_FS frame id invalid");
        return -1;
    }

    index = mm_frame_sync_find_frame_index(frame_id);
    if ((index >= 0) && (index < MM_CAMERA_FRAME_SYNC_NODES)) {
        LOGD("Removing sync frame %d", frame_id);
        memset(&fs.node[index], 0x00, sizeof(mm_channel_sync_node_t));
    }
    LOGD("X ");
    return 0;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_find_matched
 *
 * DESCRIPTION: Find  a matched sync frame from the node array
 *
 * PARAMETERS :
 *   @oldest  : If enabled, find oldest matched frame.,
 *                  If not enabled, get the first matched frame found
 *
 * RETURN     : unt32_t type of status
 *              0  -- If no matched frames found
 *              frame index: inf matched frame found
 *==========================================================================*/
uint32_t mm_frame_sync_find_matched(uint8_t oldest) {
    LOGH("E, oldest %d ", oldest);
    uint8_t i = 0;
    uint32_t frame_idx = 0;
    uint32_t curr_frame_idx = 0;
    for (i = 0; i < MM_CAMERA_FRAME_SYNC_NODES; i++) {
        if (fs.node[i].matched) {
            curr_frame_idx = fs.node[i].frame_idx;
            if (!frame_idx) {
                frame_idx = curr_frame_idx;
            }
            if (!oldest) {
                break;
            } else if (frame_idx > curr_frame_idx) {
                frame_idx = curr_frame_idx;
            }
        }
    }
    LOGH("X, oldest %d frame idx %d", oldest, frame_idx);
    return frame_idx;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_find_frame_index
 *
 * DESCRIPTION: Find sync frame index if present
 *
 * PARAMETERS :
 *   @frame_id  : frame id to be searched
 *
 * RETURN     : int8_t type of status
 *              -1  -- If desired frame not found
 *              index: node array index if frame is found
 *==========================================================================*/
int8_t mm_frame_sync_find_frame_index(uint32_t frame_id) {

    LOGD("E, frame_id %d", frame_id);
    int8_t index = -1, i = 0;
    for (i = 0; i < MM_CAMERA_FRAME_SYNC_NODES; i++) {
        if (fs.node[i].frame_idx == frame_id) {
            index = i;
            break;
        }
    }
    LOGD("X index :%d", index);
    return index;
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_lock_queues
 *
 * DESCRIPTION: Lock all channel queues present in node info
 *
 * RETURN     : None
 *==========================================================================*/
void mm_frame_sync_lock_queues() {
    uint8_t j = 0;
    LOGD("E ");
    for (j = 0; j < MAX_NUM_CAMERA_PER_BUNDLE; j++) {
        if (fs.ch_obj[j]) {
            mm_channel_queue_t *ch_queue =
                    &fs.ch_obj[j]->bundle.superbuf_queue;
            if (ch_queue) {
                pthread_mutex_lock(&ch_queue->que.lock);
                LOGL("Done locking fs.ch_obj[%d] ", j);
            }
        }
    }
    pthread_mutex_lock(&fs_lock);
    LOGD("X ");
}

/*===========================================================================
 * FUNCTION   : mm_frame_sync_unlock_queues
 *
 * DESCRIPTION: Unlock all channel queues
 *
 * RETURN     : None
 *==========================================================================*/
void mm_frame_sync_unlock_queues() {
    // Unlock all queues
    uint8_t j = 0;
    LOGD("E ");
    pthread_mutex_unlock(&fs_lock);
    LOGL("Done unlocking fs ");
    for (j = 0; j < MAX_NUM_CAMERA_PER_BUNDLE; j++) {
        if (fs.ch_obj[j]) {
            mm_channel_queue_t *ch_queue =
                    &fs.ch_obj[j]->bundle.superbuf_queue;
            if (ch_queue) {
                pthread_mutex_unlock(&ch_queue->que.lock);
                LOGL("Done unlocking fs.ch_obj[%d] ", j);
            }
        }
    }
    LOGD("X ");
}

/*===========================================================================
 * FUNCTION   : mm_channel_node_qbuf
 *
 * DESCRIPTION: qbuf all buffers in a node
 *
 * PARAMETERS :
 *   @ch_obj  : Channel info
 *   @node    : node to qbuf
 *
 * RETURN     : None
 *==========================================================================*/
void mm_channel_node_qbuf(mm_channel_t *ch_obj, mm_channel_queue_node_t *node) {
    uint8_t i;
    if (!ch_obj || !node) {
        return;
    }
    for (i = 0; i < node->num_of_bufs; i++) {
        mm_channel_qbuf(ch_obj, node->super_buf[i].buf);
    }
    return;
}
